Images and Core Web Vitals
How images affect Core Web Vitals (LCP, CLS, INP) and practical strategies to optimize for better Google rankings and user experience.
Table of Contents
- What are Core Web Vitals?
- LCP: Make your hero image load fast
- Problem: Slow hero image
- Solution 1: Optimize the image
- Solution 2: Preload the hero image
- Solution 3: Use responsive images
- Solution 4: Use modern formats
- LCP Quick Wins
- CLS: Prevent layout shift
- Problem: Images without dimensions
- Solution: Always specify dimensions
- Solution for responsive images
- CLS Quick Wins
- INP: Don’t let images block interactions
- Problem: Processing images on the main thread
- Solution: Process images off the main thread
- Solution: Lazy load offscreen images
- Solution: Defer non-critical images
- INP Quick Wins
- Real-world optimization example
- Testing Core Web Vitals
- PageSpeed Insights
- Chrome DevTools
- Chrome User Experience Report
- Quick reference: Image optimization checklist
- The bottom line
Images are usually the biggest culprit when Core Web Vitals fail. A slow-loading hero image tanks your LCP. Images without dimensions cause layout shifts (bad CLS). Heavy images block interactivity (bad INP). Fix your images, fix your scores.
What are Core Web Vitals?
Core Web Vitals are Google’s metrics for measuring user experience. They affect your search rankings. Three metrics matter:
LCP (Largest Contentful Paint) - How fast your main content loads
- Good: < 2.5 seconds
- Needs work: 2.5-4 seconds
- Poor: > 4 seconds
CLS (Cumulative Layout Shift) - How much your page jumps around while loading
- Good: < 0.1
- Needs work: 0.1-0.25
- Poor: > 0.25
INP (Interaction to Next Paint) - How fast your page responds to clicks
- Good: < 200ms
- Needs work: 200-500ms
- Poor: > 500ms
Images affect all three. Let’s fix them.
LCP: Make your hero image load fast
LCP measures when the largest visible element loads. On most sites, that’s the hero image.
Problem: Slow hero image
<!-- ❌ This kills your LCP -->
<img src="huge-hero-10mb.jpg" alt="Hero" />
If your hero image is 10 MB, LCP will be terrible.
Solution 1: Optimize the image
# Resize to actual display size
magick hero.jpg -resize 1920x -quality 85 -strip hero-optimized.jpg
# Or use modern formats
sharp('hero.jpg')
.resize(1920)
.webp({ quality: 85 })
.toFile('hero.webp');
Target: Hero images should be < 300 KB.
Solution 2: Preload the hero image
<head>
<!-- Tell browser to load this ASAP -->
<link rel="preload" as="image" href="hero.webp" fetchpriority="high" />
</head>
<body>
<img src="hero.webp" alt="Hero" loading="eager" fetchpriority="high" />
</body>
The fetchpriority attribute tells the browser to prioritize loading this resource.
Solution 3: Use responsive images
<img
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
sizes="100vw"
src="hero-800.webp"
alt="Hero"
loading="eager"
fetchpriority="high"
/>
Mobile users get smaller images = faster LCP.
Solution 4: Use modern formats
WebP/AVIF are 30-50% smaller than JPEG:
<picture>
<source srcset="hero.avif" type="image/avif" />
<source srcset="hero.webp" type="image/webp" />
<img src="hero.jpg" alt="Hero" loading="eager" fetchpriority="high" />
</picture>
LCP Quick Wins
- Optimize hero image (< 300 KB)
- Use WebP or AVIF
- Add
fetchpriority="high"andloading="eager" - Preload critical images
- Serve responsive sizes
Expected improvement: 1-3 second LCP reduction.
CLS: Prevent layout shift
CLS measures how much content jumps around. Images without dimensions are the main cause.
Problem: Images without dimensions
<!-- ❌ Causes layout shift -->
<img src="photo.jpg" alt="Photo" />
Browser doesn’t know image size → reserves no space → image loads → page jumps.
Solution: Always specify dimensions
<!-- ✅ Reserves space, no layout shift -->
<img src="photo.jpg" alt="Photo" width="800" height="600" />
Or use CSS aspect-ratio:
<img
src="photo.jpg"
alt="Photo"
style="aspect-ratio: 4/3; width: 100%; height: auto;"
/>
Solution for responsive images
<img
srcset="img-400.jpg 400w, img-800.jpg 800w"
sizes="100vw"
src="img-800.jpg"
alt="Photo"
width="800"
height="600"
style="width: 100%; height: auto;"
/>
Browser calculates aspect ratio from width/height, scales proportionally.
CLS Quick Wins
- Add width/height to ALL images
- Use aspect-ratio CSS for responsive images
- Reserve space for lazy-loaded images
- Avoid inserting content above existing content
Expected improvement: CLS score < 0.1 (good).
INP: Don’t let images block interactions
INP measures how fast pages respond to clicks. Heavy image processing can block the main thread.
Problem: Processing images on the main thread
// ❌ Blocks the main thread
images.forEach(img => {
processImage(img); // Heavy operation
});
Solution: Process images off the main thread
// ✅ Use Web Workers
const worker = new Worker('image-worker.js');
worker.postMessage({ image: imageData });
Web Workers run JavaScript in background threads, preventing UI blocking.
Solution: Lazy load offscreen images
<!-- Don't load all images at once -->
<img src="img1.jpg" loading="eager" /> <!-- Visible -->
<img src="img2.jpg" loading="lazy" /> <!-- Offscreen -->
<img src="img3.jpg" loading="lazy" /> <!-- Offscreen -->
Solution: Defer non-critical images
<!-- Load after page is interactive -->
<script>
window.addEventListener('load', () => {
document.querySelectorAll('img[data-src]').forEach(img => {
img.src = img.dataset.src;
});
});
</script>
INP Quick Wins
- Lazy load below-the-fold images
- Process images in Web Workers
- Defer non-critical image loading
- Use native lazy loading
Expected improvement: INP < 200ms (good).
Real-world optimization example
Before optimization:
- LCP: 4.2s (poor)
- CLS: 0.28 (poor)
- INP: 320ms (needs work)
Changes made:
- Optimized hero: 2.1 MB → 245 KB (WebP, Q85)
- Added width/height to all images
- Lazy loaded 40 offscreen images
- Preloaded hero image
- Used responsive images
After optimization:
- LCP: 1.8s (good) - 57% improvement
- CLS: 0.05 (good) - 82% improvement
- INP: 180ms (good) - 44% improvement
Testing Core Web Vitals
PageSpeed Insights
- Enter URL
- Run test
- Check “Lab Data” and “Field Data”
- Focus on LCP, CLS, INP
Chrome DevTools
- Open DevTools → Lighthouse
- Run audit
- Check “Performance” metrics
- Look for “Largest Contentful Paint element”
Chrome User Experience Report
Real user data from Chrome users: https://crux-compare.com/
Enter your URL to see actual user metrics.
Quick reference: Image optimization checklist
For LCP:
- Hero image < 300 KB
- Use WebP or AVIF
- Add
fetchpriority="high" - Preload critical images
- Serve responsive sizes
For CLS:
- Add width/height to all images
- Use aspect-ratio CSS
- Reserve space for lazy-loaded images
- Test on slow connections
For INP:
- Lazy load offscreen images
- Use native lazy loading
- Defer non-critical images
- Process images in workers
The bottom line
Images are the biggest factor in Core Web Vitals. Fix them and your scores improve dramatically.
Priority order:
- Optimize hero image (LCP)
- Add dimensions to all images (CLS)
- Lazy load offscreen images (INP + LCP)
- Use modern formats (LCP)
- Serve responsive sizes (LCP)
Related articles: