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

SettingNext.js 16 default
images.qualities[75]; any other quality prop is coerced to the nearest allowed value. Add values to allow more.
images.minimumCacheTTL4 hours (14400 s), up from 60 s, for images without Cache-Control. Static imports are cached as immutable.
images.imageSizes16 removed from the defaults.
images.maximumRedirects3, previously unlimited.
images.dangerouslyAllowLocalIPfalse; 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.