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.

Core Web Vitals overview

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" and loading="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:

  1. Optimized hero: 2.1 MB → 245 KB (WebP, Q85)
  2. Added width/height to all images
  3. Lazy loaded 40 offscreen images
  4. Preloaded hero image
  5. 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

https://pagespeed.web.dev/

  • Enter URL
  • Run test
  • Check “Lab Data” and “Field Data”
  • Focus on LCP, CLS, INP

Chrome DevTools

  1. Open DevTools → Lighthouse
  2. Run audit
  3. Check “Performance” metrics
  4. 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:

  1. Optimize hero image (LCP)
  2. Add dimensions to all images (CLS)
  3. Lazy load offscreen images (INP + LCP)
  4. Use modern formats (LCP)
  5. Serve responsive sizes (LCP)

Related articles: