Image Optimization for Core Web Vitals: A Practical Guide to LCP, CLS, and INP
Learn how to optimize images for Google's Core Web Vitals. Practical techniques for improving LCP, preventing CLS, and maintaining good INP scores — with measurable impact on SEO rankings.
Images account for roughly 50% of the average web page's total weight. They're the single biggest factor in how fast your page loads, how stable your layout is, and whether you pass Google's Core Web Vitals assessment.
Yet most developers treat image optimization as an afterthought — something to fix after Lighthouse flags it. This guide covers practical, actionable techniques to optimize images for all three Core Web Vitals metrics, with real numbers on the impact.
What Are Core Web Vitals?
Core Web Vitals are three metrics Google uses to measure real-world user experience. Since 2021, they've been a confirmed ranking signal in Google Search.
| Metric | Full Name | What It Measures | Good | Needs Improvement | Poor | |--------|-----------|-----------------|------|-------------------|------| | LCP | Largest Contentful Paint | Loading speed | ≤ 2.5s | ≤ 4.0s | > 4.0s | | CLS | Cumulative Layout Shift | Visual stability | ≤ 0.1 | ≤ 0.25 | > 0.25 | | INP | Interaction to Next Paint | Responsiveness | ≤ 200ms | ≤ 500ms | > 500ms |
Images directly impact all three metrics. Here's how.
LCP: The Hero Image Problem
LCP measures how long it takes for the largest visible element to render. On most pages, the LCP element is an image — a hero banner, a product photo, or a featured article image.
Why Images Are Usually the LCP Element
The browser rendering pipeline processes content in this order:
- HTML downloads and parsing begins
- CSS downloads and CSSOM is built
- JavaScript downloads and executes
- Images begin downloading
- Images render as they arrive
Images are last in the queue. By the time the browser starts downloading your hero image, it has already spent 500-1500ms on HTML, CSS, and JavaScript. If the image itself is 500 KB, that's another 500-1000ms on a typical mobile connection.
Technique 1: Compress Aggressively
The most impactful optimization is simply making images smaller.
| Original | Compressed (q80) | Savings | LCP Impact | |----------|-----------------|---------|-----------| | 1.2 MB JPEG | 180 KB WebP | 85% | -800ms | | 800 KB PNG | 120 KB WebP | 85% | -550ms | | 500 KB JPEG | 95 KB AVIF | 81% | -350ms |
LCP impact estimated on 4G mobile (10 Mbps)
Quality 80 is the threshold where compression savings are significant but visual quality remains excellent. Below 70, artifacts become visible in gradients. Above 90, the file size penalty outweighs the quality gain.
For hero images specifically, consider quality 75-80 for AVIF or WebP. Hero images are typically displayed at large dimensions where slight compression artifacts are invisible to the human eye.
Technique 2: Use Modern Formats
Format choice alone can reduce image size by 30-50% with no quality change.
| Format | Typical Size (2MP photo) | Relative Size | |--------|------------------------|---------------| | Unoptimized JPEG | 800 KB | 100% (baseline) | | Optimized JPEG (MozJPEG) | 350 KB | 44% | | WebP (q80) | 240 KB | 30% | | AVIF (q75) | 160 KB | 20% |
Serve AVIF with WebP and JPEG fallbacks using the <picture> element:
<picture>
<source srcset="hero.avif" type="image/avif" />
<source srcset="hero.webp" type="image/webp" />
<img src="hero.jpg" alt="Hero image" width="1200" height="600" />
</picture>
With Next.js, the Image component handles format negotiation automatically when configured with formats: ['image/avif', 'image/webp'] in next.config.mjs. The server detects the browser's Accept header and serves the best supported format.
Technique 3: Serve the Right Size
A common mistake: serving a 3000 × 2000 px image that displays at 1200 × 800 px. The browser downloads 2.5x more pixels than needed.
Use responsive images with srcset to serve different sizes based on the viewport:
<img
srcset="hero-600.webp 600w,
hero-900.webp 900w,
hero-1200.webp 1200w"
sizes="(max-width: 600px) 100vw,
(max-width: 900px) 900px,
1200px"
src="hero-1200.webp"
alt="Hero image"
width="1200"
height="600"
/>
This serves a 600 px wide image on mobile (saving 75% of pixels compared to the desktop version) and the full 1200 px version on desktop.
Technique 4: Preload the LCP Image
The browser doesn't discover images until it parses the HTML and CSS that reference them. For critical images, you can hint the browser to start downloading earlier:
<link rel="preload" as="image" href="hero.avif" type="image/avif" />
Or in Next.js, use the priority prop on the Image component:
<Image src="/hero.webp" alt="Hero" width={1200} height={600} priority />
This tells the browser to fetch the image with high priority during the initial page load, typically saving 200-500ms on LCP.
Technique 5: Avoid Lazy Loading Above the Fold
Lazy loading (loading="lazy") defers image loading until the image is near the viewport. This is excellent for below-the-fold images but counterproductive for the LCP image.
When you lazy-load the hero image, the browser waits until the layout is complete and the image enters the viewport before starting the download. This adds a significant delay to LCP.
Rule of thumb: Never lazy-load the first visible image. Only lazy-load images below the fold.
CLS: Preventing Image Layout Shifts
CLS measures how much the page layout shifts during loading. Images are the most common cause of layout shifts because they load asynchronously — the browser reserves no space for them until dimensions are known.
The Problem
Without explicit dimensions, the browser renders the page with zero space allocated for images. When images finally load, they push other content down. This "jumping" is what CLS measures.
A single hero image without dimensions can cause a CLS score of 0.3-0.5 — well above the "poor" threshold of 0.25.
Technique 1: Always Set Width and Height
The simplest and most effective CLS fix: always include width and height attributes on image elements.
<!-- Bad: No dimensions — causes layout shift -->
<img src="photo.webp" alt="Photo" />
<!-- Good: Dimensions set — browser reserves space -->
<img src="photo.webp" alt="Photo" width="800" height="600" />
The browser uses these attributes to calculate the aspect ratio and reserve the correct amount of space before the image loads. Even if you use CSS to make the image responsive (width: 100%; height: auto), the HTML attributes provide the aspect ratio for space reservation.
Technique 2: Use CSS aspect-ratio
For responsive images where you know the aspect ratio but not the exact pixel dimensions:
.hero-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
This reserves space with the correct aspect ratio regardless of the container width. The image scales responsively without causing layout shifts.
Technique 3: Contain Ad and Dynamic Slots
If your page includes ad banners or dynamically loaded content near images, these can cause indirect layout shifts. Reserve space for ads with minimum height:
.ad-slot {
min-height: 250px;
contain: layout;
}
The contain: layout property tells the browser that changes inside this element won't affect the layout of elements outside it.
Technique 4: Use placeholder backgrounds
For images that take time to load, show a placeholder that matches the final image dimensions. This can be a solid color, a blurred thumbnail, or a CSS gradient:
.image-container {
background-color: #f0f0f0;
aspect-ratio: 16 / 9;
}
Next.js Image component supports placeholder="blur" which automatically generates a tiny blurred version of the image as a placeholder.
INP: Images and Interaction Responsiveness
INP measures the delay between a user interaction (click, tap, keypress) and the next visual update. While images don't directly cause INP issues, image processing can block the main thread.
How Images Affect INP
Large Image Decoding
When the browser decodes a large image (converting compressed bytes to pixels), it can block the main thread for 50-200ms. If a user taps a button during this decode, the interaction is delayed.
Solution: Use the decoding="async" attribute:
<img src="photo.webp" alt="Photo" decoding="async" width="800" height="600" />
This tells the browser to decode the image off the main thread, preventing interaction delays.
JavaScript Image Processing
Client-side image processing (compression, conversion, resizing) involves heavy computation. If this runs on the main thread, it blocks all user interactions.
Solution: Use Web Workers for heavy image processing. The main thread stays responsive while the Worker handles computation in a separate thread. Professional tools use this approach — the UI remains interactive while images process in the background.
Too Many Images Loading Simultaneously
Loading 20 images at once competes for bandwidth and CPU time, potentially delaying interactions.
Solution: Lazy load below-the-fold images and limit concurrent loads:
<!-- Only loads when approaching the viewport -->
<img src="photo.webp" alt="Photo" loading="lazy" decoding="async" />
Measuring Impact
Tools for Measuring Core Web Vitals
| Tool | Type | Best For | |------|------|---------| | Lighthouse | Lab data | Development testing | | PageSpeed Insights | Lab + Field | Production assessment | | Chrome DevTools Performance tab | Lab data | Deep debugging | | Google Search Console | Field data | Real-user monitoring | | web-vitals.js | Field data | Custom monitoring |
Lab data (simulated) is useful for development. Field data (real users) is what Google uses for ranking.
Before and After: Real Impact
Here's a real-world example of image optimization impact on a content-heavy landing page:
| Metric | Before | After | Change | |--------|--------|-------|--------| | Total image weight | 4.2 MB | 680 KB | -84% | | LCP | 4.8s | 1.9s | -60% | | CLS | 0.32 | 0.01 | -97% | | INP | 280ms | 120ms | -57% | | Lighthouse Performance | 42 | 94 | +124% |
The optimizations applied:
- Converted all images from JPEG/PNG to WebP
- Compressed at quality 80
- Resized to match display dimensions (no oversized images)
- Added width/height attributes to all images
- Preloaded the hero image
- Lazy-loaded below-the-fold images
- Added
decoding="async"to non-critical images
Total development time: about 2 hours. SEO impact: measurable ranking improvement within 4-6 weeks as Google re-crawled and re-assessed the pages.
Image Optimization Checklist
Use this checklist for every page:
Compression
- [ ] All images compressed (quality 75-85 for lossy)
- [ ] Modern format served (AVIF or WebP with fallback)
- [ ] No image larger than 200 KB (except hero images)
- [ ] No oversized dimensions (max 2x display size for retina)
LCP
- [ ] LCP image identified (use Lighthouse to find it)
- [ ] LCP image preloaded or has
priorityprop - [ ] LCP image is NOT lazy-loaded
- [ ] LCP image serves correct size for viewport
CLS
- [ ] All images have explicit width and height
- [ ] Responsive images use aspect-ratio CSS
- [ ] Ad slots have reserved minimum height
- [ ] No images cause visible layout shifts on load
INP
- [ ] Non-critical images use
decoding="async" - [ ] Below-the-fold images use
loading="lazy" - [ ] Heavy image processing uses Web Workers
- [ ] No more than 3-5 images loading simultaneously above the fold
Automating Image Optimization
Manual optimization doesn't scale. For ongoing websites, automate the process:
Build-Time Optimization
Frameworks like Next.js include built-in image optimization. The <Image> component automatically:
- Generates multiple sizes for responsive serving
- Converts to modern formats (WebP, AVIF)
- Lazy loads by default
- Prevents CLS with automatic dimension detection
Content Pipeline
For blog posts and CMS content, establish a pipeline:
- Author adds images at original resolution
- Build process compresses and converts to WebP/AVIF
- Responsive variants are generated automatically
- Width/height attributes are injected
Pre-Publish Batch Processing
Before uploading to a CMS that doesn't optimize automatically, batch-process images:
- Compress all images to target quality
- Convert to the optimal format for your audience
- Resize to maximum display dimensions
- Verify file sizes are within budget
Start Optimizing
Every image on your site is an opportunity to improve Core Web Vitals. The techniques in this guide are straightforward — compress, convert, resize, and add proper HTML attributes.
Need to optimize images quickly? Krunkit's tools handle it all in the browser:
- Compress — reduce file sizes by up to 80%
- Convert — switch to WebP or AVIF for better compression
- Resize — match images to display dimensions
No uploads, no accounts, no watermarks. Drop your images, optimize, and download — your Core Web Vitals will thank you.
