Tuesday, 31 March 2026

Addressing Cumulative Layout Shift (CLS)

Addressing Cumulative Layout Shift (CLS)

In recent weeks I’ve been investigating issues highlighted by Chrome’s Lighthouse tool relating to Cumulative Layout Shift (CLS). This post explains what CLS is, why it matters, and how I resolved it across a large image-heavy site.

What is CLS?

CLS occurs when elements on a webpage move unexpectedly while the page is loading. A common example is when you try to click a button, only for the page to shift at the last moment— resulting in clicking something else entirely.

This is more than just a minor annoyance; it directly impacts usability and is now a key metric in Google’s Core Web Vitals.

CLS infographic showing layout shift before and after
A visual overview of how layout shifts occur and how to prevent them.

Why is this happening?

The primary cause of CLS on the Griffmonster Walks sites turned out to be images without defined dimensions.

When a browser encounters an image without width and height attributes, it has no way of knowing how much space to reserve during the initial page layout.

  • The browser renders the surrounding text first
  • No space is reserved for the image
  • The image loads later
  • The layout shifts to accommodate it

The Key Fix: Defining Image Dimensions

Modern browsers use width and height attributes not as styling, but as intrinsic size metadata. These values allow the browser to calculate the image’s aspect ratio before the image has even downloaded.

Before (No Dimensions)

Heading
READ MORE

After (With Dimensions)

Heading
READ MORE

Without dimensions

<img src="image.jpg">

This results in no reserved space and visible layout shift.

With dimensions

<img src="image.jpg" width="800" height="600">

The browser now knows the aspect ratio and reserves space immediately, preventing layout shift entirely.

HTML vs CSS: Clearing Up the Confusion

At first, adding dimensions directly into HTML may feel like mixing structure and styling. However, these attributes serve a different purpose:

  • HTML attributes define intrinsic dimensions (aspect ratio)
  • CSS controls how the image is displayed
<img src="image.jpg" width="800" height="600" class="responsive">

.responsive {
  width: 100%;
  height: auto;
}

In this setup, CSS controls the final size, while HTML prevents layout shift.

Other Causes of CLS

While images were the main issue here, CLS can also be caused by:

  • Ads or embedded content loading late
  • Web fonts changing text layout
  • Content injected dynamically above existing elements
  • Third-party scripts modifying the page

Automating the Fix

With thousands of images across the site, manually adding dimensions wasn’t realistic. Instead, I built an automated pipeline using XSLT and Node.js.

The process

  1. Extract all image URLs from the generated HTML using XSLT
  2. Process the URLs with a Node.js script to fetch image dimensions
  3. Store the results in an XML configuration file
  4. Use a second XSLT to inject width and height attributes back into the HTML
  5. Redeploy the updated site via the Google API

Unexpected benefits

This process also exposed a number of issues that had gone unnoticed:

  • Broken image URLs (404 errors)
  • Typos in image paths
  • Images hosted on unsupported external domains

Improving Image Loading

In addition to fixing layout shift, I improved loading behaviour using the loading attribute:

<img loading="lazy">

This defers loading of images until they are needed (i.e. when they enter the viewport).

Important exception

The main image at the top of each page was excluded from lazy loading:

<img loading="eager" fetchpriority="high">

This ensures critical content loads as quickly as possible and avoids negatively impacting Largest Contentful Paint (LCP).

Responsive Images

To further optimise performance, I implemented responsive images using srcset and sizes.

<img 
  src="image-800.jpg"
  srcset="
    image-400.jpg 400w,
    image-600.jpg 600w,
    image-800.jpg 800w"
  sizes="(max-width: 450px) 400px, (max-width: 800px) 600px, 800px"
  width="800"
  height="600"
  loading="lazy"
  alt="Example image">

This allows the browser to select the most appropriate image size for the device, reducing bandwidth usage and improving load times.

Integrating the Updates into the Workflow

The workflow was adjusted to included this process such that all future deployments will include the changes.

Final Thoughts

By ensuring all images include intrinsic dimensions and improving loading behaviour, CLS issues across the site were significantly reduced.

The key takeaway is simple:

Providing intrinsic size information allows the browser to stabilise layout during initial rendering.

It’s a relatively small change with a significant impact on both performance metrics and real-world user experience.

0 comments:

Post a Comment