Image Optimisation: Adopting next/image Across the Platform
Image Optimisation: Adopting next/image Across the Platform
Released in v1.0.88 — 2026-03-26
Overview
Starting with v1.0.88, BlockManOS uses the Next.js Image component (next/image) as the standard for all image rendering. The Avatar component is the first to be migrated as part of the PERF-02 performance initiative. This establishes the technical foundation — remotePatterns configuration, dimension conventions, and the priority API — that all future image work in the platform will build on.
What Changed
Avatar Component
The Avatar component previously rendered user photos using a plain <img> tag. It now uses <NextImage> from next/image.
Before:
<img
src={src}
alt={name ?? "Avatar"}
className={cn("rounded-full object-cover", sizes[size], className)}
/>
After:
<NextImage
src={src}
alt={name ?? "Avatar"}
width={px}
height={px}
priority={priority}
className={cn("rounded-full object-cover", SIZE_CLASS[size], className)}
/>
The component API is unchanged for existing call sites. The only new prop is priority.
Component Props
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | null | — | Remote URL or local path for the avatar image. |
name | string | null | — | Used for the alt attribute and initials fallback. |
email | string | null | — | Used for the initials fallback when name is absent. |
size | "sm" | "md" | "lg" | "md" | Controls rendered dimensions (24 px / 32 px / 48 px). |
className | string | — | Additional Tailwind classes applied to the image. |
priority | boolean | false | Set to true for above-the-fold avatars to add a preload hint and skip lazy loading. |
Size Reference
size prop | Rendered px | Tailwind class |
|---|---|---|
sm | 24 × 24 | size-6 |
md | 32 × 32 | size-8 |
lg | 48 × 48 | size-12 |
Explicit dimensions are required by next/image to reserve intrinsic layout space. This eliminates Cumulative Layout Shift (CLS) caused by images loading into unsized containers.
Using the priority Prop
By default, next/image lazy-loads images — they are only fetched when they enter the viewport. For avatars that appear immediately on page load (e.g. the logged-in user's avatar in the top navigation bar), lazy loading causes an LCP penalty because the browser doesn't know to fetch the image early.
Set priority={true} to override this behaviour:
// Top navigation — logged-in user's own avatar
<Avatar
src={session.user.image}
name={session.user.name}
size="md"
priority={true} // ← adds <link rel="preload">, skips lazy loading
/>
// Content list — avatars of other users, below the fold
<Avatar
src={member.image}
name={member.name}
size="sm"
// priority omitted — defaults to false (lazy loaded)
/>
Only use priority for avatars that are genuinely visible on first paint. Overusing it defeats the purpose of lazy loading.
Remote Patterns Configuration
Next.js requires all external image hostnames to be explicitly allowlisted in next.config.ts before next/image will load from them. The following domains are now configured:
// next.config.ts
images: {
remotePatterns: [
{ protocol: "https", hostname: "www.gravatar.com", pathname: "/avatar/**" },
{ protocol: "https", hostname: "gravatar.com", pathname: "/avatar/**" },
{ protocol: "https", hostname: "lh3.googleusercontent.com" },
{ protocol: "https", hostname: "avatars.githubusercontent.com" },
{ protocol: "https", hostname: "*.amazonaws.com" },
{ protocol: "https", hostname: "*.r2.cloudflarestorage.com" },
{ protocol: "https", hostname: "utfs.io" },
],
},
If you add a new external image source to the platform, add a corresponding entry to remotePatterns in next.config.ts. Next.js will block any unlisted hostname at build/request time.
Why next/image Matters
| Benefit | Detail |
|---|---|
| Format negotiation | Automatically serves WebP or AVIF to browsers that support them, falling back to the source format. No manual conversion needed. |
| Server-side resizing | Resizes images to the exact display dimensions on the server. A 2 MB profile photo is never sent to the browser for a 32 px avatar. |
| CLS elimination | Explicit width/height let the browser reserve space before the image loads, preventing layout shifts. |
| Lazy loading by default | Off-screen images are deferred, reducing initial page weight. |
| LCP optimisation | The priority prop adds a <link rel="preload"> hint for above-the-fold images, improving LCP scores. |
Health Endpoint Middleware Bypass
As a small related change in this release, /api/health was added to the authentication middleware bypass list in src/middleware.ts. The health check endpoint now always returns a response without requiring a valid session, making it suitable for use by load balancers, uptime monitors, and deployment health checks.
Bypassed routes (no auth required):
/api/inngest/api/webhooks/api/trpc/api/auth/api/health(added in v1.0.88)