vercel/next.js
Image optimization
Two halves: a React <Image> component on the client, and an HTTP image-optimization endpoint at /_next/image on the server. The endpoint resizes, recodes, and caches images on demand. The client component generates the right srcset and sizes to consume the endpoint.
Components
graph LR
Source[<img src='/wikis/next-js/systems/hero.jpg'>] --> Component
Component[next/image client<br/>image-component.tsx] --> Srcset[generates srcset]
Browser -- /_next/image?url=hero.jpg&w=640 --> Endpoint
Endpoint[image-optimizer.ts<br/>35 KB] --> Resize[Sharp / Squoosh]
Resize --> Cache[Disk cache<br/>incremental-cache]
Cache --> Endpoint
Endpoint --> BrowserClient component
packages/next/src/client/image-component.tsx (15 KB) is the component user code imports as next/image. It produces an <img> (or <picture> when needed) with:
srcsetcovering the configureddeviceSizesandimageSizesfromnext.config.js.sizesbased on thefill,width, orsizesprops.loading="lazy"by default;priorityprop opts into eager loading and a preload tag.- Built-in placeholder support (blur, color).
- Layout shift prevention through the
widthandheightrequirements.
Configuration of the client component reads from images-manifest.json written at build time.
Server endpoint
packages/next/src/server/image-optimizer.ts (35 KB) implements the /_next/image?url=...&w=...&q=... endpoint. It:
- Validates the request URL against the configured
images.remotePatterns,images.domains, andimages.localPatternsallowlists. - Looks up the response in the disk cache (
packages/next/src/server/lib/incremental-cache/). - On miss, fetches the source image (local file or remote URL), pipes it through the optimizer, and writes the result to the cache.
- Streams back the optimized output with the appropriate
Cache-ControlandContent-Typeheaders.
The actual image processing uses Sharp when available (Node's preferred path, much faster) and falls back to a WASM resizer (@squoosh/lib style) when Sharp isn't installed.
Optimization options
| Option | Source |
|---|---|
| Output format | Decided per request, based on Accept header (AVIF, WebP, fallback to original) |
| Quality | q query param, default 75 |
| Width | w query param, must be in deviceSizes or imageSizes |
| Cache TTL | images.minimumCacheTTL config + remote Cache-Control |
The framework explicitly rejects arbitrary widths to prevent cache poisoning.
Caching
The endpoint reuses the framework's incremental cache (packages/next/src/server/lib/incremental-cache/). On disk, optimized variants live under .next/cache/images/.
For deployments where the host doesn't want to run image optimization itself, images.loader can be set to 'cloudinary', 'imgix', 'vercel', or a custom loader. The loader-based path generates URLs for an external service; the framework then bypasses the local optimizer.
Static assets
The image component handles three source types:
- Static imports (
import logo from './logo.png') — width and height are known at build time, the component avoids layout shift natively. - Local public files — width and height must be supplied by the user.
- Remote URLs — must match
images.remotePatterns. Width and height must be supplied.
Build-time static analysis happens in packages/next/src/build/static-paths/ and the asset path tracking lives in the build manifests.
Edge runtime
When middleware or an edge route emits an image URL, the optimizer endpoint still runs in the Node runtime — the optimizer is too heavy for the edge sandbox. Edge code can reference images and emit URLs but cannot perform optimization itself.
Configuration
User config in next.config.js under the images block:
{
images: {
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
formats: ['image/avif', 'image/webp'],
minimumCacheTTL: 60,
domains: ['example.com'],
remotePatterns: [{ protocol: 'https', hostname: '**.cdn.example.com' }],
loader: 'default',
path: '/_next/image',
},
}Schema validation happens in packages/next/src/server/config-schema.ts.
Integration points
- The build pipeline writes
images-manifest.jsonconsumed by both client and server. - The endpoint shares the disk cache with ISR (see caching).
- The client component reads global config from the manifest at hydration time.
Entry points for modification
- To change the optimizer pipeline:
packages/next/src/server/image-optimizer.ts. - To change client behavior:
packages/next/src/client/image-component.tsx. - To add a new format or codec:
packages/next/src/server/image-optimizer.tsplusnext.config.jsschema inconfig-schema.ts. - To add a built-in loader:
packages/next/src/shared/lib/image-loaders/.
Key source files
| File | Purpose |
|---|---|
packages/next/src/client/image-component.tsx |
Client <Image> component |
packages/next/src/server/image-optimizer.ts |
/_next/image endpoint |
packages/next/src/server/og/ |
next/og image generation (separate from this system) |
packages/next/src/shared/lib/image-loaders/ |
Built-in remote loaders |
packages/next/src/server/lib/incremental-cache/ |
Shared disk cache |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.