/ Image Optimisation Fixes / Image CLS

How to Fix Image-Caused Layout Shift

Images are the single biggest cause of Cumulative Layout Shift on the web. Late-loading images push surrounding content down as they arrive, creating the jarring "page jumping" experience users hate and Google penalises. CLS is one of the three Core Web Vitals, and image CLS is fixable with attribute-level changes. This guide covers the reserved-space patterns, aspect-ratio fallbacks for unknown dimensions, placeholder strategies, and verification.

1. Understand the layout shift

What happens without reserved space:

  1. Browser parses HTML, encounters <img> with no dimensions
  2. Image element collapses to 0x0 size pending download
  3. Surrounding content (text below, footer, etc.) renders flush against where the image will be
  4. Image downloads, decodes, takes its natural size
  5. Everything below gets pushed down by however tall the image is
  6. That push is the layout shift, contributing to CLS

With reserved space:

  1. Browser parses HTML, encounters <img width="800" height="600">
  2. Image element reserves a 4:3 box (computed from attributes)
  3. Surrounding content renders below the reserved space
  4. Image downloads, displays in the already-reserved box
  5. No layout shift

2. Identify CLS contributors

Step 1
Lighthouse CLS audit
Lighthouse Performance → expand CLS metric → "Avoid large layout shifts". Each shift listed with its score contribution and the offending element.
Step 2
DevTools layout shift visualisation
DevTools Performance tab → record load → look for blue rectangles in the Experience track. Each represents a layout shift event. Click to see which element shifted and by how much.
Step 3
Web Vitals extension
Web Vitals extension shows live CLS during browsing. Visit your pages, watch the number rise; pinpoint which interactions or scrolls trigger shifts.

3. Fix 1: Add dimensions to every image

<!-- Wrong -->
<img src="/hero.jpg" alt="...">

<!-- Right -->
<img src="/hero.jpg" alt="..." width="1200" height="675">

Use intrinsic dimensions — the natural pixel dimensions of the source file. Browser computes aspect ratio (16:9 here) and reserves matching space. CSS can scale display size; aspect ratio stays the same.

Pair with CSS for responsive:

img {
  max-width: 100%;
  height: auto;
}

4. Fix 2: aspect-ratio for variable dimensions

For images with unknown dimensions at template render time (user uploads, dynamic content), CSS aspect-ratio reserves the right space:

<div class="image-frame">
  <img src="/uploads/abc.jpg" alt="...">
</div>

<style>
.image-frame {
  aspect-ratio: 16 / 9;
  background: #f0f0f0;
  overflow: hidden;
}
.image-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
</style>

Browser reserves the 16:9 frame immediately. Image loads inside it, cropped or scaled by object-fit. Regardless of actual image dimensions, the frame stays at 16:9 — no shift.

5. Fix 3: placeholder backgrounds

Even with reserved space, an empty rectangle then sudden image appearance can feel jarring. Soft transition with a placeholder colour:

<img src="/hero.jpg"
     width="1200" height="675"
     alt="..."
     style="background: #f0f0f0;">

Or use LQIP (Low Quality Image Placeholder)

Tiny base64-encoded blur of the full image. Loads instantly with the HTML, full image fades in over it.

<img src="data:image/jpeg;base64,iVBORw0KGgoA..."
     data-src="/hero.jpg"
     width="1200" height="675"
     alt="...">

<!-- Modern: BlurHash or thumbhash -->
<div class="placeholder" style="background-image: url(blur.svg)">
  <img src="/hero.jpg" width="1200" height="675">
</div>

Next.js Image with blur placeholder

import Image from 'next/image';
import heroImage from './hero.jpg';

<Image src={heroImage} alt="..." placeholder="blur" />
// Auto-generates blurred placeholder, served inline as base64

6. Fix 4: handle late-injected images

JavaScript-injected images (ads, embedded widgets, lazy-loaded media) often cause CLS because they appear after layout settles. Reserve space ahead of time:

<!-- Ad slot reserves 300x250 before ad loads -->
<div class="ad-slot" style="width: 300px; height: 250px; background: #f5f5f5">
  <script>
    googletag.cmd.push(function() {
      googletag.display('ad-slot-1');
    });
  </script>
</div>

Same pattern for embeds:

<!-- YouTube embed with reserved space -->
<div style="aspect-ratio: 16/9; background: #000">
  <iframe src="..." loading="lazy" width="100%" height="100%"></iframe>
</div>

7. Common pitfalls

Pitfall 1: CSS-only dimensions

<!-- Bad: CSS won't prevent CLS -->
<img src="/hero.jpg" style="width: 1200px; height: 675px">

CSS applies after stylesheet loads. HTML attributes work immediately. Use both for safety — attributes for layout, CSS for responsive sizing.

Pitfall 2: removed dimensions in CMS templates

// WordPress example — some themes strip dimensions
function add_image_dimensions($content) {
  // Bad theme code might do this
  return preg_replace('/width="\d+"\s+height="\d+"/', '', $content);
}

// Don't strip them. Browsers need them.

Pitfall 3: inconsistent srcset aspect ratios

<!-- Bad: mobile variant has different ratio -->
<img srcset="/mobile-400x600.jpg 400w,
             /desktop-1200x675.jpg 1200w"
     width="1200" height="675">
<!-- Browser reserves 16:9 but mobile image is 2:3 -->
<!-- Image renders cropped or distorted -->

For art-directed crops, use picture with separate sources and dimensions per breakpoint.

8. The whole-page strategy

One page-wide template change typically fixes most image CLS:

9. Verify CLS improvement

Step 1
Lab CLS in Lighthouse
Run Lighthouse Mobile on representative pages. CLS should be under 0.1. If still high, check the listed contributors — often a fonts issue or a single missed image.
Step 2
Field CLS in Search Console
Real-user CLS data takes 2-4 weeks to reflect changes. Search Console → Core Web Vitals. Track percentage of URLs in Good vs Needs Improvement vs Poor. Successful CLS work moves URLs from Poor to Good.
Step 3
PageSpeed Insights live test
pagespeed.web.dev shows both lab and field CLS per URL. Run it on key pages before and after deployment.
💡 The 5-minute fix: add width and height attributes to the image-rendering helper or component in your codebase. One template change patches every image site-wide. Most sites with CLS problems are missing this one thing.

🖼️ Re-run the Image Optimisation audit

Verify CLS findings and measure improvement.

Run Image Audit →
Related Guides: Image Optimisation Fixes  ·  Fix Image Dimensions  ·  CLS Debugger Fixes  ·  Image Optimisation Guide
💬 Got a problem?