Image optimization for Core Web Vitals

Images are usually the bottleneck on Core Web Vitals scores. Three vitals (LCP, INP, CLS) are all touched by image decisions. Here's exactly what to do to hit Google's "Good" thresholds.

Core Web Vitals are Google's set of metrics that quantify "real user experience" — specifically loading speed, responsiveness, and visual stability. They directly influence search rankings and conversion rates. They're also the area where most websites have the most room to improve.

For most sites, images are the largest factor in two of the three Vitals. This article covers the specific techniques to address each Vital, with targets you should aim for.

The three Core Web Vitals

  • LCP (Largest Contentful Paint) — how long until the largest visible element loads. Should be under 2.5 seconds.
  • INP (Interaction to Next Paint) — how long until the page responds to a click or tap. Should be under 200 milliseconds.
  • CLS (Cumulative Layout Shift) — how much elements jump around as the page loads. Should be under 0.1.

Images affect LCP directly, CLS strongly, and INP indirectly. Let's go through each.

LCP: the image performance vital

On most modern websites, the LCP element is a hero image — a large image at the top of the page. The time until that image renders determines your LCP score, and it's almost entirely about image optimization.

What to optimize

  1. File size: a 2 MB hero image takes 4× as long to download as a 500 KB one. Hard cap your hero images at 200 KB. Aspirational target: 100 KB.
  2. Format: WebP at quality 80 is roughly 30% smaller than JPG at quality 85 with identical perceived quality. AVIF is another 30% smaller but with longer decode times.
  3. Dimensions: a hero image shown at 1200px wide doesn't need to be larger than 2400px. Use srcset to serve smaller images to phones.
  4. Loading priority: add fetchpriority="high" to the hero image's <img> tag. This tells the browser to prioritize this image's download over others.
  5. Preloading: add a <link rel="preload" as="image"> for the hero image. The browser can start downloading it before parsing the rest of the HTML.

What NOT to do

  • Don't lazy-load the hero image. Lazy loading defers the download until the image is near the viewport — but the hero image is already in the viewport. loading="lazy" on the hero image hurts LCP.
  • Don't put the hero image inside a slow JavaScript-rendered component. If the image only appears after a framework hydrates, LCP suffers.
  • Don't serve the same hero to phones. A 1920×1080 hero on a 412×892 phone screen is wasted bytes.

The HTML for a fast hero image

<link rel="preload" as="image" href="/hero-1600.webp">
...
<picture>
  <source srcset="/hero-800.webp 800w, /hero-1200.webp 1200w, /hero-2000.webp 2000w"
          sizes="100vw"
          type="image/webp">
  <img src="/hero-1200.jpg"
       alt="Description of the hero"
       width="2000" height="1000"
       fetchpriority="high">
</picture>

The width and height attributes prevent layout shift (more on CLS below). The fetchpriority="high" tells the browser to prioritize this image. The preload in the head starts download even earlier.

CLS: stop the layout shift

Cumulative Layout Shift happens when elements move around as the page loads. The biggest culprit: images without dimensions. The browser doesn't know how tall an image is until it downloads it, so the layout reflows when the image arrives.

The fix is one line

Every image needs width and height attributes set on the <img> tag. The browser uses these to reserve space before the image loads.

<img src="photo.jpg" alt="..." width="1200" height="800">

The numbers don't have to match the displayed size. They define the aspect ratio. With width and height set, the browser reserves a 1200:800 box and fills it with the loaded image when ready, with no jumping.

Responsive images need the same treatment

When using CSS like img { width: 100%; height: auto; }, the width/height attributes still matter — modern browsers use them to compute aspect ratio. Set them, even if CSS overrides the actual rendered size.

Other CLS causes

  • Web fonts that load late and cause text reflow (use font-display: optional or preload fonts).
  • Ad slots that insert content after page load (reserve space with min-height).
  • Late-loading widgets, embeds, banners.

But for most sites, sizing images correctly fixes 80% of the CLS issue.

INP: keep the main thread free

Interaction to Next Paint measures how quickly the page responds to user input. Image decoding is part of this — if a user taps a button while a large image is decoding on the main thread, the response is delayed.

Decode hints

Browsers normally decode images synchronously on the main thread. You can hint that an image can be decoded asynchronously:

<img src="photo.jpg" alt="..." decoding="async">

This tells the browser to decode the image off the main thread when possible. Combine with loading="lazy" for below-the-fold images.

Avoid massive images

An image at 5000×4000 has 20 million pixels to decode. Even if the file is only 800 KB (because it's heavily compressed), decoding it takes meaningful CPU time. Limit source dimensions to what you actually display.

Lazy loading: only for below the fold

The loading="lazy" attribute defers image download until the image is near the viewport. This helps overall page weight and INP.

Critical rule: only use lazy on below-the-fold images. The first 1-3 images that appear on the screen should load eagerly (default behavior). Lazy-loading them hurts LCP.

<!-- Hero image — load eagerly -->
<img src="hero.webp" alt="..." width="1600" height="900" fetchpriority="high">

<!-- Article image partway down — lazy load -->
<img src="diagram.webp" alt="..." width="800" height="500" loading="lazy">

Image CDNs and adaptive delivery

For sites with many images, an image CDN (Cloudinary, imgix, Cloudflare Images, etc.) handles a lot of this automatically. They serve the right format and size based on the requesting device, with on-the-fly transformations.

For smaller sites, you can achieve the same result with pre-generated variants and srcset. The work is more upfront but the runtime cost is zero.

How to measure

Don't optimize blind. Use these tools to measure:

  • PageSpeed Insights — pagespeed.web.dev. Reports real-world Core Web Vitals from Chrome users plus lab data.
  • Chrome DevTools Lighthouse — open DevTools, switch to Lighthouse tab, run an audit. Lab-based scoring.
  • Web Vitals extension — shows live Vitals on any page you visit.
  • Search Console — Google's own Core Web Vitals report for your site, based on real user data.

A pre-launch checklist

  1. Every <img> has width and height.
  2. Hero image is under 200 KB.
  3. Hero image uses WebP (with JPG fallback if needed).
  4. Hero image has fetchpriority="high".
  5. Hero image is preloaded.
  6. Below-the-fold images have loading="lazy".
  7. Responsive images use srcset with multiple sizes.
  8. PageSpeed Insights shows "Good" for LCP, INP, and CLS.

Hit all eight and your image-related Web Vitals will be in the top 10% of websites.


Prepare your hero images with pictoolkit's resize, compress, and WebP converter. All processing in your browser.

Keep reading

Related articles