How to Fix Slow LCP with the Next.js Image Component

This guide is part of Next.js Performance, within Framework Performance. The Next.js Image component (next/image) generates responsive srcsets, serves modern formats through the built-in optimiser or a custom loader, sets dimensions to prevent layout shifts, and lazy-loads images by default. That last default is the most common cause of slow LCP in Next.js apps: unless the hero image is marked with priority (or fetchPriority="high" and loading="eager" in newer versions), it is lazy-loaded and discovered late.

Other configuration problems compound it: a missing or wrong sizes prop makes the browser download images far larger than displayed; fill images without a sizes default to 100vw; the optimiser transforms images on first request, which can take hundreds of milliseconds on a cache miss; and high default quality settings waste bytes.

LCP p75 for a hero image by configuration Bar chart comparing LCP for a Next.js hero image with different Image component settings. LCP p75 for a hero image by configuration Default (lazy, no sizes) 3400ms priority, no sizes 2600ms priority + correct sizes 2100ms + CDN loader (cached) 1700ms LCP good

Rapid Diagnosis

  • Inspect the LCP image's HTML: loading="lazy" means priority is missing.
  • Check the downloaded width against the displayed width in DevTools (Network panel image preview shows intrinsic size).
  • Check /_next/image response times on first request versus cached.
  • Check for a preload link in the head for the LCP image.

Root Cause Analysis

1. Lazy loading by default. The hero waits until layout determines it is in view.

2. Missing or wrong sizes. The browser picks large candidates.

3. Cold optimiser cache. First requests transform images synchronously.

4. Client-rendered hero. The image URL is known only after JavaScript runs.

Step-by-Step Resolution

1. Mark the LCP image as priority

javascript
import Image from 'next/image';
<Image src={product.heroUrl} alt={product.name} width={1200} height={900}
       priority sizes="(max-width: 768px) 100vw, 50vw" quality={70} />
// trade-off: priority preloads the image with high fetch priority; mark only the
// one or two images likely to be LCP, or they compete with each other.

2. Set sizes to match the layout

sizes should describe the image's rendered width at each breakpoint. For fill images, always set sizes.

3. Use a CDN-backed loader or cache the optimiser

For high-traffic sites, configure a custom loader that points at an image CDN, or ensure the built-in optimiser's output is cached at the CDN with a long minimumCacheTTL.

javascript
// next.config.js — custom loader for an image CDN.
module.exports = { images: { loader: 'custom', loaderFile: './image-loader.js', formats: ['image/avif', 'image/webp'] } };
// image-loader.js
export default function loader({ src, width, quality }) {
  return `https://img.example-cdn.com${src}?w=${width}&q=${quality || 70}&auto=format`;
}

4. Render the hero on the server

Make sure the hero image's URL is in the server-rendered HTML (Server Components or SSR), not determined after client-side data fetching.

next/image LCP checklist Checks for configuring the Next.js Image component for a fast LCP. next/image LCP checklist priority on the LCP image Preloaded with high fetch priority, not lazy Accurate sizes Matches rendered width at each breakpoint Cached or CDN-backed optimisation No slow transforms on the critical path Server-rendered URL Discoverable in the initial HTML 1 2 3 4

Verification

In DevTools, the LCP image should have fetchpriority="high", no loading="lazy", and a preload link in the head. The downloaded image width should be close to displayed width × DPR. Image responses should come from cache (check x-nextjs-cache or CDN headers). In the field, LCP for the template should improve, particularly the resource load delay and load duration parts.

Worked Example: A Real Estate Listing Site

A real estate site's listing pages used next/image with fill for the main photo, no priority and no sizes. The browser lazy-loaded a 3840px image for a 600px slot on mobile. LCP p75 was 3.4 seconds. Adding priority and sizes="(max-width: 768px) 100vw, 60vw" cut the image from 680KB to 110KB on mobile and removed the lazy-load delay. Moving optimisation to an image CDN via a custom loader removed 300–700ms transform delays on cache misses for new listings. LCP p75 fell to 1.8 seconds.

Priority, fetchPriority and Preloading

In Next.js, priority does two things: it disables lazy loading and adds a preload link with high fetch priority in the document head. Newer Next.js versions also expose fetchPriority and loading directly, and may change how priority works; check the documentation for your version. Either way, the goal is the same: the LCP image must be discoverable in the initial HTML, requested early, and not deprioritised. Avoid marking many images as priority — they compete for bandwidth, which can make the real LCP image slower.

Placeholder Blur and LCP

placeholder="blur" shows a blurred low-resolution version while the full image loads. It improves perceived loading but does not count as the LCP: LCP fires when the full image renders. The inline blur data adds a little HTML weight. It is a good choice for below-the-fold and gallery images; for the hero, focus on making the full image arrive fast.

Image props and their LCP effect How common next/image props affect LCP when used on the hero image. Image props and their LCP effect Prop LCP effect Use on hero? priority preload + high priority yes sizes right-sized download yes quality 60-75 fewer bytes yes placeholder blur perceived only optional loading lazy (default) late discovery never

Finding the LCP Image per Template

The LCP element varies by template and viewport: the product photo on product pages, a hero banner on the homepage, sometimes a headline instead of an image on mobile. Check each key template at mobile and desktop widths using the Performance panel's LCP marker or the web-vitals attribution build, which reports the LCP element's selector and URL in the field. Mark only that element as priority, and revisit after redesigns. When the LCP image differs by breakpoint, use getImageProps with a picture element so each breakpoint's image is the one preloaded.

Common Mistakes

  • No priority on the hero. The single most common Next.js LCP problem.
  • fill without sizes. Defaults to full viewport width.
  • Many priority images. Competing high-priority downloads.
  • Uncached optimisation. Slow transforms on every cache miss.

Edge Cases

Static imports. Statically imported images get automatic dimensions and blur placeholders; still set priority and sizes.

Remote images. Configure remotePatterns to allow optimisation of remote sources.

Art direction. Use the getImageProps helper with a picture element for different crops per breakpoint.

Unoptimized images. unoptimized skips the optimiser; ensure the source is already sized and in modern formats.

FAQ

Why is my Next.js hero image lazy-loaded?

next/image lazy-loads by default. Add priority (or the equivalent loading and fetch priority props in your version) to the LCP image.

What should sizes be?

A media-query list describing the rendered width at each breakpoint, such as (max-width: 768px) 100vw, 50vw.

Is the built-in optimiser fast?

Cached responses are fast; first requests transform images and can be slow. Cache at the CDN or use an image CDN loader.

Should I enable AVIF?

Yes, with formats: ['image/avif', 'image/webp']. AVIF encoding is slower on cache misses, so caching matters more.

Does next/image prevent CLS?

Yes, when dimensions are set (width and height, or fill within a sized container).

How many images should have priority?

Usually one — the likely LCP image. At most a couple when the LCP element differs by viewport.

Can I use next/image for background images?

Use fill with object-fit: cover in a positioned container instead of CSS backgrounds, so the image is discoverable and optimised.

What quality setting should I use?

Around 60–75 is a good range for photos with modern formats; validate visually for your content.

Does next/image work with static exports?

The default optimiser needs a server. For static exports, use a custom loader pointing at an image CDN, or pre-generate sized images.

Should I use unoptimized for SVGs?

Yes. SVGs do not benefit from raster optimisation; serve them directly.