Core Web Vitals are Google’s attempt to turn “page feels slow” into a measurable number — and since 2021, those numbers have been a direct input into search ranking. If your pages don’t pass the thresholds, you’re losing rankings to competitors whose pages do.
For most content sites, images are the biggest lever on Core Web Vitals — bigger than JavaScript bundle size, server response time, or font loading. The hero image alone is usually the element that decides whether the page hits its 2.5-second Largest Contentful Paint threshold.
This guide covers which Vital each image-related fix moves, by how much, and in what order to tackle them.
The three Vitals in 2026
Core Web Vitals have changed since 2021. The current set:
- Largest Contentful Paint (LCP) — how long it takes the largest above-the-fold element to render. Threshold: 2.5 seconds (good), 4.0 seconds (poor). On most pages, the LCP element is the hero image.
- Interaction to Next Paint (INP) — how long the page takes to visually respond to a user input, measured across the whole session. Threshold: 200 ms (good), 500 ms (poor). Replaced First Input Delay (FID) in March 2024.
- Cumulative Layout Shift (CLS) — how much the page jumps around as it loads. Threshold: 0.1 (good), 0.25 (poor).
Images touch all three, but they touch LCP hardest. We’ll go in that order.
How images affect each Vital
LCP. On 70–80% of webpages, the LCP element is an image — typically the hero or the first large in-content image. The size of that image, the format it’s served in, the priority it’s loaded with, and whether the server starts sending it before the rest of the page is parsed all directly determine LCP. A 1.2 MB hero JPEG on a 4G connection takes roughly 2.4 seconds to download before it even starts decoding — so your LCP starts in the yellow before any other code runs.
INP. Images don’t directly cause INP problems — JavaScript does — but heavy image decoding on the main thread blocks input handling on lower-end devices. A 20 MB PNG that has to be decoded on a $150 Android phone can stall the main thread for hundreds of milliseconds. Compressed, properly-sized images keep the main thread free for input handling.
CLS. This one is purely about layout reservation. If you don’t tell the browser how big an image will be (via width/height attributes or CSS aspect-ratio), the page reflows when the image arrives, and you take a CLS hit. The fix isn’t compression — it’s markup.
The fixes, in priority order
1. Compress every image that ships
Start here. If your CMS is exporting JPEGs at the libjpeg defaults, you’re shipping files 20–40% larger than necessary, for zero quality benefit. Modern encoders (MozJPEG for JPEG, libwebp for WebP, libaom/rav1e for AVIF) produce visibly smaller files at perceptually identical quality.
For a one-time pass over an existing image library, you can run files through our image compressor — it uses the same MozJPEG / libwebp / OxiPNG encoders the desktop CLIs use, compiled to WebAssembly, so the files never leave your machine. For ongoing compression, wire a modern encoder into your build pipeline or your CDN’s image-transformation layer.
Expected impact: a typical 800 KB hero JPEG re-encoded with MozJPEG at quality 75 lands around 320–450 KB. On a 4G connection, that’s 0.4–0.6 seconds shaved off LCP, before any other change.
2. Use a modern format
Once you’ve compressed, the next lever is the format itself. Re-encoding a JPEG as WebP at the same perceptual quality typically saves another 25–35%. AVIF saves another 40–60%.
We covered the trade-offs in detail in JPG vs WebP vs AVIF in 2026, but the short version: use WebP as your default and AVIF for hero images, with a JPEG fallback served via <picture>:
<picture>
<source srcset="hero.avif" type="image/avif" />
<source srcset="hero.webp" type="image/webp" />
<img src="hero.jpg" alt="…" width="1600" height="900" />
</picture>
Expected impact: another 100–300 KB off a typical hero, for another 0.2–0.5 seconds of LCP improvement on mobile.
3. Right-size for the viewport
The single most wasteful thing you can do is ship a 3000-pixel-wide image to a 375-pixel-wide phone screen. The browser will scale it down for display, but you’ve already paid the download cost for all the pixels you’re throwing away.
Serve multiple sizes via srcset and sizes:
<img
src="hero-800.webp"
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1600.webp 1600w, hero-2400.webp 2400w"
sizes="(min-width: 1024px) 50vw, 100vw"
width="1600"
height="900"
alt="…"
/>
The browser picks the smallest variant that’s still big enough for the layout. A phone gets a 400-wide image; a retina laptop gets a 2400-wide one. Both feel sharp; neither pays for bytes they don’t need.
Expected impact: on mobile, 30–60% reduction in image bytes downloaded for the typical responsive layout.
4. Mark up dimensions to lock CLS to zero
For every <img>, set width and height attributes. These don’t constrain the rendered size — your CSS still does that — but they let the browser reserve the right amount of space before the image loads, so the layout doesn’t jump.
<img src="..." width="1600" height="900" alt="..." />
For images sized purely by CSS, use aspect-ratio:
img.hero {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
}
On a typical content page, adding this is often the difference between a CLS of 0.0 and 0.15, with no other changes needed.
5. Set fetchpriority="high" on the LCP image
By default, browsers don’t know which image is the LCP element. They start downloading images in DOM order, which means an icon in the header gets priority over the hero below it.
The fetchpriority attribute, supported in all modern browsers as of 2024, lets you tell the browser explicitly:
<img src="hero.webp" fetchpriority="high" alt="..." />
Use it on exactly one image per page — the one most likely to be the LCP element. Setting it on everything defeats the purpose.
Expected impact: 0.2–0.8 seconds off LCP when the LCP image isn’t first in the DOM. The hero of a page with a complex header is the classic case.
6. Lazy-load below-the-fold images
Every image below the fold should have loading="lazy":
<img src="..." loading="lazy" alt="..." />
This doesn’t help your above-the-fold LCP directly, but it frees up bandwidth and main-thread time for the things that do. It also reduces total page weight, which matters for users on metered connections.
Don’t lazy-load the LCP image. That’s the opposite of what you want — you want it eager and high-priority.
7. Preload the LCP image
For the single image you care about most — the hero — add a <link rel="preload"> in the <head>:
<link
rel="preload"
as="image"
href="hero.webp"
imagesrcset="hero-400.webp 400w, hero-800.webp 800w, hero-1600.webp 1600w"
imagesizes="100vw"
fetchpriority="high"
/>
This tells the browser to start fetching the hero before it’s finished parsing the rest of the HTML. On pages where the hero is the LCP element, this can shave another 0.2–0.4 seconds.
8. Serve from a CDN that’s close to your users
If your images are served from a single origin in one region, users on the other side of the world are paying 100–300 ms of round-trip latency before the first byte of the image arrives. A CDN with global PoPs drops that to 20–50 ms.
This isn’t an image-compression concern per se, but it’s the next-biggest lever once the formats and sizes are right.
What “good” looks like in numbers
A well-optimised image-heavy page in 2026 should look roughly like this:
- Hero image: 50–150 KB WebP or AVIF, preloaded,
fetchpriority="high", dimensions in markup - In-content images: 20–80 KB each, lazy-loaded, dimensions in markup, served via
srcset - Icons: SVG, inlined where possible
- Total image weight, above the fold: under 300 KB
- LCP on 4G mobile: under 2.0 seconds
- CLS: under 0.05
If your numbers are nowhere near these, you have room to move. In practice, most production sites are missing at least two or three of the fixes above — and each is a small change.
A checklist
Run through this list on your highest-traffic page:
- Every image has been re-encoded with a modern encoder (MozJPEG, libwebp, libaom)
- Every image is served as WebP (or AVIF + WebP + JPEG fallback) where possible
- Every image has
widthandheightattributes set - Below-the-fold images have
loading="lazy" - The LCP image has
fetchpriority="high" - The LCP image is preloaded via
<link rel="preload"> - Responsive variants are served via
srcsetandsizes - Animated GIFs have been replaced with
<video>elements - Images are served from a CDN
If you can tick all nine, your image-related Core Web Vitals will be in the green band on almost any page.
Where to start
Open your highest-traffic page. Run it through Google’s PageSpeed Insights. Look at what it identifies as the LCP element — almost certainly an image. Run that image through an image compressor, check whether you’re shipping it in a modern format, and verify that the <img> tag has dimensions, fetchpriority, and a <link rel="preload">.
For most pages, that one element accounts for the bulk of the LCP problem. Fix it, ship it, and re-measure in 28 days when the Chrome User Experience Report rolls forward.
Image compression isn’t the only performance work that matters, but the effort-to-result ratio is hard to beat. A hero image shipped once gets downloaded by everyone who visits, so a few hundred kilobytes shaved off it adds up quickly.
