DevgainsDevgainsDevgains
All articles

Image Optimization Is the Single Biggest LCP Win

·4 min read·Updated Jun 30, 2026
Image Optimization Is the Single Biggest LCP Win

Photo: Unsplash

If you only have time to optimize one thing for perceived speed, optimize your images. On the majority of web pages, the Largest Contentful Paint element — the biggest thing visible in the initial viewport, the one Core Web Vital that correlates most directly with "this page feels slow" — is an image. A hero banner, a product photo, an article's cover.

That's a gift, because it means one element decides your score. Fix the loading of that single image and you've moved the metric that matters most. (LCP is one of three Core Web Vitals — see the complete guide to LCP, INP, and CLS for the full picture.) Here's the playbook, roughly in order of impact.

Serve the right format

JPEG and PNG are decades old. Modern formats compress dramatically better at the same visual quality. AVIF typically beats JPEG by 50% or more on file size; WebP is a safe, near-universal middle ground. The <picture> element lets the browser pick the first format it supports and fall back gracefully:

<picture>
  <source srcset="hero.avif" type="image/avif" />
  <source srcset="hero.webp" type="image/webp" />
  <img src="hero.jpg" alt="Mountain at sunrise" width="1600" height="900" />
</picture>

The browser downloads exactly one of these. Old browsers get the JPEG; everyone else gets something far smaller. This change alone routinely cuts hero image weight in half with no visible quality loss.

Stop shipping desktop images to phones

A 1600px-wide hero on a 390px phone screen is wasted bytes — the device downloads four times the pixels it can display. Responsive images via srcset and sizes let the browser choose an appropriately sized file for the viewport and pixel density:

<img
  src="hero-800.jpg"
  srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"
  sizes="(max-width: 600px) 100vw, 50vw"
  width="1600" height="900"
  alt="Mountain at sunrise"
/>

The MDN guide to responsive images walks through w descriptors and the sizes attribute in detail. The mental model: srcset lists the files you have, sizes tells the browser how big the image will render so it can pick before layout is even complete.

Don't make the LCP image wait

The biggest LCP mistakes aren't about file size — they're about timing. Two anti-patterns dominate. First, lazy-loading the hero. loading="lazy" is great for below-the-fold images, but on your LCP element it actively delays the metric, because the browser defers the request. Never lazy-load the LCP image. Mark it eager and high-priority instead:

<img src="hero.avif" fetchpriority="high" alt="..." width="1600" height="900" />

Second, hiding the image behind JavaScript. If your hero is a CSS background-image injected by a component, or an <img> whose src is set after hydration, the browser's preload scanner can't discover it early. Put the real <img> in the server-rendered HTML so the request starts during the initial parse. For critical images you can go further with a preload hint in the <head>.

The fastest image request is the one the browser starts before it finishes reading the page. That only happens when the image is a plain <img> in your initial HTML with a discoverable src. JavaScript-driven images always start late.

Always reserve the space

Images that load without declared dimensions cause layout shift: content jumps as the image pops in, hurting your CLS score and annoying users mid-read. The fix is trivial and you've seen it in every example above — always set width and height attributes (or an aspect-ratio in CSS). The browser reserves the correct box before the image arrives, so nothing jumps:

img {
  height: auto;        /* keep aspect ratio */
  aspect-ratio: 16 / 9;
}

This costs nothing and prevents one of the most jarring perceived-quality problems on the web. There is no reason to ship an image without dimensions.

Compress, then verify

Even in a modern format, images are often saved at quality settings far higher than anyone can perceive. Run them through a compressor — sharp in a build step, Squoosh for one-offs, or a tool from your framework. A quick Node example using sharp:

import sharp from "sharp";
 
await sharp("hero.png")
  .resize(1600)
  .avif({ quality: 55 })
  .toFile("hero.avif");

Then verify in the field. LCP is a real-user metric, so measure it with the web-vitals library and watch your p75, not your laptop. The numbers will tell you whether the hero is genuinely the bottleneck or whether something upstream — a slow server response, render-blocking CSS — is delaying it.

Takeaways

  • The LCP element is usually an image. Fixing it moves the metric users feel most.
  • Serve AVIF/WebP with <picture> fallbacks; it routinely halves file size with no visible loss.
  • Use srcset/sizes so phones don't download desktop-sized images.
  • Never lazy-load the LCP image, and keep it a real <img> in server HTML with fetchpriority="high" so the request starts early.
  • Always set width/height to prevent layout shift, compress aggressively, and verify p75 LCP in the field.
4 min read

Read next