How images affect Core Web Vitals. Practical fixes for LCP, CLS, and INP regressions caused by images, with measurement tools and benchmarks.
How Images Affect Core Web Vitals
Images are the single largest contributor to Core Web Vitals regressions on most websites. They dominate page weight, they are usually the Largest Contentful Paint element, and unconstrained dimensions are the most common cause of layout shift. This guide walks through how each of the three Core Web Vitals is affected by images and what to do about it.
For the broader image performance picture, see WebP Optimisation. For the loading techniques referenced here, see WebP Lazy Loading.
The three Core Web Vitals
Google currently tracks three metrics as Core Web Vitals:
- Largest Contentful Paint (LCP) — how quickly the largest meaningful content element renders. Target: < 2.5s at the 75th percentile.
- Cumulative Layout Shift (CLS) — how much visible content unexpectedly shifts during page load. Target: < 0.1 at the 75th percentile.
- Interaction to Next Paint (INP) — how quickly the page responds to user input. Target: < 200ms at the 75th percentile. (Replaced First Input Delay as a Core Web Vital in March 2024.)
All three are field-measured: Google records them from real users, not from synthetic Lighthouse runs. Lighthouse approximates them but the authoritative score comes from the Chrome User Experience Report.
LCP: the image one
For most content pages, the LCP element is an image — typically the hero, the first product photo, or the article header. That means LCP is usually a question of "how fast can we get this one image on screen?"
The factors that determine image LCP, in order of impact:
1. File size
Smaller files arrive faster. WebP gives you a 25–35% size win over JPEG at equivalent quality, which compounds across slow connections. See WebP Compression Settings for parameter tuning.
A 1200×675 hero image should target:
- JPEG: 60–120 KB
- WebP (lossy q=80): 40–80 KB
- AVIF: 30–60 KB
If your hero image is larger than this, that is your first fix.
2. Discovery time
The browser cannot fetch the LCP image until it finds it. The faster it discovers, the faster LCP completes.
Discovery time is improved by:
- Placing the LCP image in HTML, not JavaScript-rendered DOM.
- Using
<link rel="preload">in<head>for the LCP image. - Avoiding lazy-loading the LCP image (see WebP Lazy Loading).
- Using
fetchpriority="high"on the LCP<img>.
3. Connection priority
The browser fetches many resources in parallel and prioritises them. By default, an in-viewport <img> has medium priority — below CSS and fonts, above scripts. To promote it:
<img
src="/img/hero.webp"
alt="..."
width="1600"
height="900"
fetchpriority="high"
decoding="async"
/>
4. Decode time
After the bytes arrive, the browser still has to decode them to pixels. Large images decode slowly on low-end mobile devices. decoding="async" keeps the decode off the main thread so it does not block other rendering, but it does not make the decode itself faster — for that, you need a smaller image.
5. Server response time
If your server takes 800ms to respond, no client-side optimisation will hit a 2.5s LCP budget consistently. Time-to-first-byte (TTFB) is the floor below which LCP cannot go. Audit with curl -w "%{time_starttransfer}\n" -o /dev/null -s YOUR_URL.
What to ship
For a typical LCP image:
<head>
<link
rel="preload"
as="image"
href="/img/hero-1200.webp"
imagesrcset="/img/hero-800.webp 800w, /img/hero-1200.webp 1200w, /img/hero-1800.webp 1800w"
imagesizes="100vw"
type="image/webp"
fetchpriority="high"
/>
</head>
<body>
<picture>
<source
type="image/webp"
srcset="/img/hero-800.webp 800w, /img/hero-1200.webp 1200w, /img/hero-1800.webp 1800w"
sizes="100vw"
/>
<img
src="/img/hero-1200.jpg"
width="1600"
height="900"
alt="..."
fetchpriority="high"
decoding="async"
/>
</picture>
</body>
Note: no loading="lazy" on the LCP image. That is the single most common LCP regression — explained in detail in WebP Lazy Loading.
CLS: the dimensions one
Cumulative Layout Shift measures content that moves unexpectedly during load. Images cause CLS when they load after the surrounding content has already laid out, pushing that content down.
The fix is structural, not stylistic: tell the browser the image's intrinsic dimensions before it loads.
Always include width and height
<img src="..." width="1200" height="675" alt="..." />
The browser uses the ratio of width:height to reserve space before the image arrives. When the image loads, it fills that space without shifting surrounding content.
This works even when the image is responsively sized in CSS:
img {
max-width: 100%;
height: auto;
}
The width and height attributes are not fixed dimensions in this case — they are the aspect ratio hint. The browser scales the reserved space to the actual rendered size.
Use aspect-ratio for CSS-only image components
.product-image {
aspect-ratio: 4 / 3;
width: 100%;
background-color: #f5f5f5;
}
Useful for images whose intrinsic dimensions are not known at HTML render time, or for placeholder boxes that will receive an image dynamically.
Reserve space for late-loading hero images
If your hero contains text that overlays an image, the layout of the text depends on the image's height. Even with width/height on the image, font loading can cause the text itself to reflow.
Mitigations:
- Use
font-display: optionalorfont-display: swapwithsize-adjustto minimise font-related shifts. - Set a stable height on the hero container regardless of content size.
CLS triggers that are not images
CLS is not exclusively an image problem. Other contributors:
- Ads inserted late into the DOM.
- Web fonts loading after fallback fonts rendered.
- Cookie banners and consent dialogs.
- Embeds (YouTube, Twitter, Instagram) without reserved space.
Lighthouse identifies the largest shift sources by element. Use that to prioritise.
INP: the interaction one
INP measures the latency between a user input (click, tap, keypress) and the next paint that reflects the interaction. Images do not directly trigger INP regressions — INP is dominated by JavaScript main-thread time — but image work can compete for main-thread time.
The image-related contributors to INP:
Synchronous decoding
If a JavaScript click handler swaps in a new image and the browser decodes it synchronously, the decode time gets attributed to the interaction. Use decoding="async" (or call .decode() explicitly off the interaction path) to keep decode off the critical interaction.
Image-heavy paint work
If an interaction causes many new images to enter the viewport (a filter that reveals a grid, for example), the cumulative decode and layout work can hold up the next paint. The mitigations:
- Lazy-load images that are not yet visible (see WebP Lazy Loading).
- Use small placeholder thumbnails until the user settles on a view.
- Throttle filter inputs so the browser does not start work that will be invalidated.
Image processing during interaction
Client-side image manipulation (canvas operations, filters, cropping) on the main thread is a direct INP regression. Move the work to a Web Worker or OffscreenCanvas.
Measurement tools
Field data (real users)
- Chrome User Experience Report (CrUX) — the authoritative source. Aggregated 28-day data from real Chrome users. Available through PageSpeed Insights and the CrUX API.
- Search Console Core Web Vitals report — Google's own dashboard of your site's vitals, page-by-page.
- Real User Monitoring (RUM) tools — paid options like SpeedCurve, Calibre, and DebugBear give you continuous tracking. Self-hosted options include
web-vitals(the Google library) reporting to your own analytics.
Lab data (synthetic)
- Lighthouse (in Chrome DevTools) — single-run synthetic test. Good for debugging a specific change. Not authoritative for ranking purposes.
- PageSpeed Insights — combines field data and a synthetic Lighthouse run on the same page. Useful for one-shot diagnostics.
- WebPageTest — most detailed synthetic testing tool. Multiple runs, custom network throttling, filmstrip view of paint events.
Per-page debugging
In Chrome DevTools:
- Open the Performance panel.
- Record a page load.
- Look at the "Timings" track for LCP and Layout Shift markers.
- The LCP marker is annotated with the element that was the LCP — confirm it is the image you expected.
Benchmarks by site type
Rough LCP targets seen in well-optimised sites:
| Site type | LCP (75th percentile) |
|---|---|
| Marketing / brand | < 1.5s |
| Ecommerce product | < 2.0s |
| News / publishing | < 2.0s |
| SaaS dashboard | < 2.5s |
| Image-heavy gallery | < 2.5s |
If your LCP is materially above these, image optimisation almost always gets you there.
Common regressions
Switching to a slower-encoded format
Replacing WebP with AVIF can regress LCP on low-end devices because AVIF decoding is slower than WebP decoding. The byte savings compete with the decode cost. Measure on real devices, not Lighthouse.
Adding a hero video
Videos do not contribute to LCP (the LCP candidate is the largest visible image or text block). Adding a video may push the LCP element later in the page, shifting the metric to a different element with different load characteristics.
CMS auto-resize stripping dimensions
Some CMSes serve <img> without width and height after automatic resize. Audit your CMS output and ensure dimensions survive the transformation.
Lazy-loading the hero
Covered in WebP Lazy Loading but worth repeating: this is the single most common regression after a "performance audit" recommends adding loading="lazy" indiscriminately.
Where to go from here
- WebP Optimisation — the broader performance picture
- WebP Lazy Loading — loading strategy detail
- WebP Compression Settings — encoder tuning
- Image SEO Best Practices — SEO companion to performance
- Convert content: PNG to WebP, JPG to WebP
Core Web Vitals is a measurement framework, not an optimisation strategy. Use it to find the regressions, then apply the patterns in the linked guides to fix them.