Available for project

MID NOV 2026

(GMT+0)
September 2, 2026/Nextjs/5 min read

Next.js Image Optimization: Getting LCP Under 2.5 Seconds

The hero image usually decides your Largest Contentful Paint. Here is how I use next/image, the sizes attribute, preloading and modern formats to get LCP under 2.5 seconds on real phones.

DS
Written byDanish Sohail
Next.js Image Optimization: Getting LCP Under 2.5 Seconds

On most sites I audit, the Largest Contentful Paint element is an image: the hero photo, a product shot, a banner. Which means the fastest way to pass LCP is usually not a new server or a new framework. It is getting that one image right. Next.js gives you a very good tool for the job in next/image, and it is also easy to use in a way that quietly makes things worse. Here is how I set it up.

The target: 2.5 seconds on a real phone

Google treats an LCP of 2.5 seconds or less as good, measured at the 75th percentile of real visits. That last part matters. Your office laptop on fibre is not the benchmark; a mid-range phone on mobile data is. LCP is one of three Core Web Vitals alongside INP and CLS, and if the terms are new, start with my Core Web Vitals guide for business owners.

LCP time breaks down into four parts: the server response, the delay before the image starts downloading, the download itself, and rendering. Image optimisation mostly attacks the middle two.

What next/image does for you

The Image component resizes images on demand, serves modern formats to browsers that support them, lazy-loads anything offscreen by default and generates a srcset so each device can download a sensible size. That removes the most common mistake I find: a 4,000-pixel photo straight off a camera, displayed 400 pixels wide.

What it does not do is decide which image matters most. That is your job.

Photo-editing software open on a laptop, preparing hero images for a fast Next.js site

Get the sizes attribute right

If an image uses fill or a responsive CSS width, the browser needs the sizes attribute to pick the right file from the srcset. Without it, Next.js assumes the image could be the full width of the viewport, so a card image that only takes a third of the screen can download at full-screen resolution.

<Image
  src={hero}
  alt="Team working in the studio"
  fill
  sizes="(max-width: 768px) 100vw, 50vw"
  priority
/>

Read it as: on screens up to 768 pixels wide, this image fills the viewport; above that, it takes half. Match it to your actual CSS layout, which should already follow responsive design best practices. On the sites I fix, a correct sizes value alone often shrinks the hero download considerably, especially on phones.

Preload the LCP image

By default next/image lazy-loads, which is right for most images and wrong for the hero. A lazy-loaded LCP image waits until the browser has worked out the layout before it even starts downloading. Mark the one or two images visible on first load with priority — recent Next.js versions deprecate it in favour of a preload prop, so check which your version uses. Either way, the image is loaded eagerly with a high fetch priority and a preload hint, so the download starts as early as possible.

Only do this for images that are genuinely above the fold on common screen sizes. Mark everything as a priority and nothing is.

Serve AVIF and WebP

Out of the box, Next.js serves WebP to browsers that support it. You can add AVIF, which is usually smaller again at similar visual quality:

// next.config.ts
images: {
  formats: ["image/avif", "image/webp"],
}

The trade-off is that AVIF takes longer to encode, so the first request for each size is slower until the result is cached. For most business sites that is worth it. Watch the quality setting too: the default is sensible, and pushing it higher to make photos look "crisper" is a reliable way to inflate file sizes for no visible gain.

Width, height and layout shift

Every image needs its dimensions known before it loads, or the page jumps when it arrives. With static imports, Next.js reads the width and height for you. For remote images, pass width and height, or use fill inside a container with a fixed aspect ratio. That is what keeps CLS under 0.1. Layout shift from images is one of the performance mistakes I fix most often, and it is almost always a two-minute change.

Canon DSLR camera on a white background, the usual source of oversized website photos

Remote images and where they are cached

Images from a CMS or storage bucket have to be allowed in next.config through images.remotePatterns. Be specific — the exact hostname and a path prefix — rather than allowing any domain, otherwise anyone can use your optimiser to resize images from anywhere and put it on your hosting bill.

Also check where optimised images are cached. On some hosts the results sit behind a CDN close to the visitor; on others, every uncached resize hits a single server. If images are slow on first view and fast afterwards, that is the cache warming up. Your choice of host and how your CDN caches matter here as much as the component itself.

Blur placeholders and art direction

placeholder="blur" shows a tiny blurred version while the full image loads. It is automatic for statically imported images; for remote ones you supply a blurDataURL. It improves perceived speed, not the LCP number itself, so treat it as polish rather than a fix.

Art direction — a tall crop on mobile, a wide one on desktop — is better handled with a <picture> element than by hiding one image with CSS, because an eagerly loaded image still downloads when it is hidden. Next.js provides getImageProps() to generate optimised sources for each crop inside that <picture> element.

Hero videos need posters

With a background video, the poster image or the first frame can become the LCP element. Either way, the browser needs something to paint quickly. Always set a poster, optimise it like any other hero image, and keep the video itself short, muted and compressed. If an autoplaying video on mobile data pushes LCP well past the threshold, it is costing you more than it adds. I usually serve a static image on small screens and the video only on larger ones.

Measure it properly

  • Lab: Lighthouse and the Performance panel in Chrome DevTools show which element is the LCP element and how long each phase took. Test with mobile throttling switched on.
  • Field: PageSpeed Insights shows real-user data from the Chrome UX Report once your site has enough traffic, and Search Console groups URLs by status. This is the data Google actually uses.
  • Your own data: Next.js has a useReportWebVitals hook for sending real LCP values to your analytics, so you spot regressions before Google does.

Fix images first, then look at server response time and render-blocking fonts. Images are rarely the only problem, but they are usually the biggest one — and next/image is one of the reasons I keep recommending the framework, as I explain in the App Router for business websites.

LCP stuck above 2.5 seconds? Send me the page and I will tell you exactly what is slowing it down.

Next.js Image Optimization: Get LCP Under 2.5 Seconds