Overview
next/image generates responsive srcset candidates, serves the best format the browser accepts, reserves layout space to prevent CLS, and lazy-loads by default. Failures come from a missing sizes value, the wrong LCP loading hint, or remote sources the config does not allow. Next.js 16 changed several image defaults, listed below.
Use next/image with explicit dimensions or fill
Set src, alt, and either width and height (the intrinsic size, used for the aspect ratio) or fill inside a positioned parent. alt is required; use "" only for decorative images (see accessibility).
import Image from "next/image";
<Image src="/hero.jpg" alt="Mountain at dawn" width={1600} height={900}
sizes="(max-width: 768px) 100vw, 1280px" />Set sizes to the rendered width
Without sizes, the browser assumes the image is 100vw and Next.js generates only a small density-based srcset. With sizes, Next.js generates the full width-based srcset and the browser picks the smallest adequate candidate. Describe the layout, not the source file.
<Image src="/card.jpg" alt="..." fill
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" />A wrong value either over-downloads or serves a blurry image.
Mark the LCP image, and not priority
priority is deprecated as of Next.js 16. For the hero image, set loading="eager" and fetchPriority="high", which covers most cases. Use preload (adds a <link rel="preload"> in <head>) only when the same image is always the LCP element; do not use it when several images could be the LCP depending on viewport, and do not combine it with loading or fetchPriority.
<Image src={post.cover} alt={post.title} fill sizes="100vw" loading="eager" fetchPriority="high" />Apply this to one image per page. See core-web-vitals for the LCP target.
Choose a placeholder
A static import (import cover from "./cover.jpg") gets a blurDataURL generated at build time, so placeholder="blur" works as is. A remote image needs an explicit blurDataURL (a tiny base64 image) or no placeholder.
Allow remote and query-string sources explicitly
Remote images are optimized only for hosts listed in images.remotePatterns. images.domains is deprecated. Avoid wildcard hostnames and restrict pathname; an open optimizer is a free image proxy for attackers.
// next.config.ts
export default {
images: {
remotePatterns: [{ protocol: "https", hostname: "images.example.com", pathname: "/uploads/**" }],
localPatterns: [{ pathname: "/assets/**", search: "?v=1" }], // local src with a query string
},
};Know the Next.js 16 defaults
| Setting | Next.js 16 default |
|---|---|
images.qualities | [75]; any other quality prop is coerced to the nearest allowed value. Add values to allow more. |
images.minimumCacheTTL | 4 hours (14400 s), up from 60 s, for images without Cache-Control. Static imports are cached as immutable. |
images.imageSizes | 16 removed from the defaults. |
images.maximumRedirects | 3, previously unlimited. |
images.dangerouslyAllowLocalIP | false; enable only on a private network, and only after weighing the SSRF risk. |
images.formats | ["image/webp"]; AVIF is opt-in. |
AVIF encodes about 50 percent slower and compresses about 20 percent smaller than WebP, per the Next.js docs, and each format is cached separately. Enable ["image/avif", "image/webp"] only for stable, high-traffic images.
Choose a loader that fits the workload
The default loader runs on the host. For a CDN or CMS that ships its own variants (Cloudinary, Imgix, Cloudflare Images), set loader: "custom" with loaderFile, or pass loader on one component. Use unoptimized for pre-sized assets and static product photography. Hosted optimizers bill by usage, so check the host’s current pricing before enabling optimization for high-volume user uploads; see vercel. For SEO rules see image-seo.