Concept
The beginner framing: images are frequently the single largest category of bytes on a page, and a naive <img> tag gives the browser no help deciding what size to actually serve, when to load it, or how to avoid the layout jump while it's loading.
next/image, confirmed capabilities against this app's own bundled docs
import Image from "next/image";
export default function ProductPage() {
return (
<Image
src="/hero.jpg"
alt="Product hero shot"
width={800}
height={600}
/>
);
}Confirmed: next/image automatically serves correctly-sized images per device (avoiding shipping a 4000px-wide image to a 400px-wide mobile viewport), converts to modern formats like WebP where supported, prevents layout shift automatically (the required width/height reserve the correct aspect-ratio space before the image loads, directly the CLS fix covered in the Core Web Vitals topic), and lazy-loads by default using native browser lazy loading, only fetching images as they approach the viewport.
Confirmed version-currency catch: priority is deprecated in favor of preload
// ❌ DEPRECATED as of Next.js 16, confirmed against this app's own bundled docs:
<Image src="/hero.jpg" alt="Hero" width={1200} height={600} priority />
// ✅ Current replacement:
<Image src="/hero.jpg" alt="Hero" width={1200} height={600} preload />Confirmed directly against this app's own installed Next.js 16 documentation: the priority prop, widely known and taught as THE way to mark an above-the-fold/LCP image, was deprecated starting in Next.js 16, replaced by a new preload boolean prop specifically "to make the behavior clear." The new prop's own guidance is genuinely more nuanced than a blanket "set this on your hero image": preload should be used specifically when an image IS the LCP element, is above the fold, and you want it discoverable in <head> before the browser's HTML parser reaches it in <body>, but the docs explicitly recommend loading="eager" or fetchPriority="high" INSTEAD of preload in most other cases, and explicitly warn against preload when multiple images could be the LCP element depending on viewport size (a common real scenario with responsive hero layouts).
The mechanism: an image-specific instance of the same preload technique
Content-Security-Policy aside, this is the SAME <link rel="preload"> mechanism
demonstrated in the network-waterfall preset, just generated automatically by
the framework's `preload` prop rather than hand-written in raw HTML.next/image's preload prop, when set, inserts a real <link rel="preload" as="image" ...> tag into the document <head>, mechanically identical to the hand-written preload hint that moved LCP from 2400ms to 1500ms in the network-optimization topic's confirmed example. The framework prop is a convenience for generating that exact same browser hint correctly (with the right as, imagesrcset, etc. attributes derived from the component's other props), not a different underlying mechanism.
Try It
Predict the outcome before checking the solution.
// A page with a responsive layout: on mobile, a small square logo is the
// largest element; on desktop, a wide hero banner is the largest element.
<Image src="/logo.png" alt="Logo" width={80} height={80} preload />
<Image src="/hero-banner.jpg" alt="Hero" width={1600} height={600} />Given the confirmed guidance that preload shouldn't be used "when you have multiple images that could be considered the LCP element depending on the viewport," is this specific usage correct?
Solution
Likely incorrect, or at least suspect, this is exactly the scenario the confirmed documentation explicitly warns against. On mobile, the small logo might genuinely be the largest visible element (if the hero banner is hidden or much smaller in a responsive layout), while on desktop, the hero banner is almost certainly the actual LCP element, but only the LOGO has preload set here. If the hero banner is actually the LCP element on most viewports (likely, given it's described as the "wide hero banner"), preloading the small logo instead provides no LCP benefit and may even compete for early bandwidth with the resource that actually matters. The fix requires either identifying which image is ACTUALLY the LCP element for the primary target viewport/breakpoint (and preloading that one instead), or, per the docs' broader guidance, using fetchPriority="high" on the hero banner as the generally-recommended approach when viewport-dependent LCP candidacy makes a single blanket preload choice ambiguous.
Implement It Yourself
Build a minimal responsive srcset generator, the actual mechanism behind "serve the right size image per device":
function generateSrcSet(baseName, widths) {
return widths.map((w) => `${baseName}?w=${w} ${w}w`).join(", ");
}
function generateSizes(breakpoints) {
// breakpoints: [{ maxWidth: 640, imageWidth: '100vw' }, { imageWidth: '50vw' }]
return breakpoints
.map((bp) => (bp.maxWidth ? `(max-width: ${bp.maxWidth}px) ${bp.imageWidth}` : bp.imageWidth))
.join(", ");
}
This is the actual underlying mechanism next/image generates automatically from a single width/height: instead of one fixed image URL, the browser receives a SET of candidate URLs at different widths plus a sizes hint describing how much viewport space the image will actually occupy at different breakpoints, the browser then downloads only the ONE candidate that best matches the real device, avoiding shipping a needlessly large image to a small viewport without requiring any JavaScript-side device detection at all.
Under the Hood
The preload prop's underlying mechanism is a direct, confirmed instance of the exact <link rel="preload"> technique demonstrated mechanically in Network Optimization's network-waterfall preset, same browser hint, same LCP-timing effect, just framework-generated rather than hand-written. And every claim in this topic about next/image's specific current behavior (the priority-to-preload deprecation, format conversion, sizing) is verified against the SAME bundled documentation tree covered as this course's standing verification method for the entire Next.js domain, extended here to Next.js Performance's broader framework-specific optimization coverage.
Common Mistakes
1. Using the deprecated priority prop based on older tutorials/training material
<Image src="/hero.jpg" priority /> {/* ❌ deprecated in Next.js 16, confirmed */}Confirmed against this app's own installed version, priority still likely works (deprecated APIs are often kept functional for a transition period) but signals working from stale documentation; preload (used deliberately, per its narrower guidance) or fetchPriority="high" are the current recommended approaches.
2. Setting preload on multiple images, or on an image that isn't actually the LCP element for the primary viewport
<Image src="/img1.jpg" preload />
<Image src="/img2.jpg" preload /> {/* ❌ multiple preloads compete for early bandwidth priority */}As shown in Try It, preload is meant for THE specific LCP-candidate image, not a general "load this fast" flag to sprinkle across several images; the documentation explicitly warns against exactly this pattern when viewport-dependent LCP candidacy is ambiguous.
3. Omitting width/height on a next/image, defeating its automatic layout-shift prevention
<Image src="/photo.jpg" alt="Photo" fill /> {/* only valid with an explicitly SIZED parent container */}Without explicit dimensions (or a properly-sized parent when using fill), the browser has no way to reserve the correct layout space before the image loads, directly reintroducing the CLS problem next/image's dimension requirement exists specifically to prevent.
Best Practices
- Use
preloaddeliberately, only for the confirmed LCP element, per the current, narrower guidance, not as a general "make this load faster" flag. - Prefer
loading="eager"orfetchPriority="high"overpreloadfor images that need early loading but aren't unambiguously the single LCP element, matching the current documented recommendation. - Always provide
width/height(or a properly-sizedfillcontainer), this is what enablesnext/image's automatic CLS prevention. - Verify current prop names/behavior against the actual installed Next.js version's docs before writing image-optimization code, as demonstrated by the -to- deprecation, framework APIs in this space change across versions.
Performance Tips
- The
srcset/sizesmechanism (whichnext/imagegenerates automatically) shifts the "which size to download" decision to the BROWSER, evaluated against the actual device, this is strictly more accurate than any server-side or JavaScript-based device-guessing approach, and requires zero client-side computation. - Automatic WebP conversion (where supported) typically produces meaningfully smaller file sizes than an equivalent JPEG/PNG at the same visual quality, a real, close-to-free win for any image
next/imagealready handles, requiring no manual format-conversion pipeline.
