How to Fix Cumulative Layout Shift (CLS): Causes, Diagnosis, and Solutions

What Is Cumulative Layout Shift and Why Should You Fix It?

Cumulative Layout Shift (CLS) is a Core Web Vital metric that measures visual stability on a web page. Every time a visible element moves unexpectedly after the page starts rendering, the CLS score goes up. A high score means users experience content jumping around, buttons moving out from under their cursor, and text reflowing while they try to read it.

Google uses CLS as a ranking signal. If your pages have poor CLS scores, you are likely losing both search visibility and user trust. The good news is that most CLS problems are predictable and fixable once you know where to look.

In this guide, we will walk through every common cause of poor CLS, show you exactly how to diagnose each issue, and give you concrete before-and-after code fixes you can apply today.

What Is a Good CLS Score?

Rating CLS Score What It Means
Good 0.1 or less Minimal unexpected movement. Users have a stable experience.
Needs Improvement Between 0.1 and 0.25 Noticeable shifts that may frustrate some visitors.
Poor Above 0.25 Severe layout instability. Likely hurting rankings and conversions.

Your goal should always be to keep CLS at 0.1 or below on the 75th percentile of page loads, for both mobile and desktop.

The Most Common Causes of Poor CLS Scores

Before you can fix cumulative layout shift, you need to understand what triggers it. Here are the most frequent culprits, ranked roughly by how often they appear in real-world audits.

  1. Images and videos without explicit dimensions
  2. Web fonts causing text reflow (FOIT/FOUT)
  3. Dynamically injected content (ads, banners, cookie notices, embeds)
  4. Late-loading third-party scripts
  5. CSS or JavaScript that resizes elements after render
  6. Animations that trigger layout changes

We will cover each one in detail below with diagnosis steps and code-level fixes.

How to Diagnose CLS Issues

Before applying fixes, you need to pinpoint exactly which elements are shifting and when. Here are the best tools for the job.

1. PageSpeed Insights (Lab + Field Data)

Go to PageSpeed Insights and enter your URL. The tool will show your CLS score along with a list of elements that contributed to layout shifts. Look for the “Avoid large layout shifts” diagnostic. It will list each shifting element by its CSS selector, making it easy to find in your code.

2. Chrome DevTools Performance Panel

This is the most powerful way to see layout shifts in real time.

  1. Open Chrome DevTools (F12 or right-click then Inspect).
  2. Go to the Performance tab.
  3. Check the “Web Vitals” checkbox in the settings.
  4. Click Record, then reload the page.
  5. Stop recording and look for the red or orange markers labeled “Layout Shift” in the timeline.
  6. Click on each shift event to see which DOM node moved, how far it moved, and the shift score for that event.

3. Chrome DevTools Rendering Tab

For a quick visual overlay:

  1. In DevTools, press Ctrl+Shift+P (or Cmd+Shift+P on Mac) and type “Layout Shift Regions.”
  2. Enable Layout Shift Regions.
  3. Reload the page. Shifted areas will flash with a blue highlight.

4. Google Search Console

Search Console groups your URLs by Core Web Vitals status. If you see pages flagged with CLS issues, prioritize those first. Search Console uses field data from real users, so it reflects actual conditions better than lab tools alone.

5. Web Vitals JavaScript Library

For ongoing monitoring, you can add Google’s web-vitals library to your site and log CLS data to your analytics platform. This gives you continuous real-user measurement.

Fix #1: Images and Videos Without Dimensions

Why It Causes CLS

When the browser encounters an <img> or <video> tag without explicit width and height, it does not know how much space to reserve. The element starts at zero height, and once the file loads, the content below it gets pushed down.

Before (Bad)

<img src="hero-banner.jpg" alt="Hero banner">

No dimensions. The browser reserves zero space until the image loads.

After (Fixed)

<img src="hero-banner.jpg" alt="Hero banner" width="1200" height="600">

With width and height attributes, the browser calculates the aspect ratio and reserves the correct space before the image downloads. Combine this with responsive CSS:

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

This ensures the image scales responsively while maintaining the reserved aspect ratio space.

Modern Alternative: The aspect-ratio CSS Property

.hero-image {
  width: 100%;
  aspect-ratio: 2 / 1;
}

This works even if the HTML attributes are missing, but using both HTML attributes and CSS is the safest approach for maximum browser compatibility.

Quick Checklist for Images

  • Every <img> tag has width and height attributes.
  • Every <video> and <iframe> has explicit dimensions or a wrapper with a fixed aspect ratio.
  • Lazy-loaded images below the fold still have dimensions set.
  • CSS includes max-width: 100%; height: auto; for responsive scaling.

Fix #2: Web Fonts Causing Text Reflow

Why It Causes CLS

When a custom web font loads after the page is already rendered, the browser may swap the fallback font for the custom font. Because the two fonts often have different character widths, line heights, and spacing, text blocks reflow and push surrounding content around. This is known as Flash of Unstyled Text (FOUT) or Flash of Invisible Text (FOIT).

How to Fix It

A. Use font-display: swap with size-adjust

@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom-font.woff2') format('woff2');
  font-display: swap;
  size-adjust: 105%;
  ascent-override: 90%;
  descent-override: 20%;
  line-gap-override: 0%;
}

The font-display: swap property tells the browser to show the fallback font immediately and swap to the custom font once it loads. The size-adjust, ascent-override, descent-override, and line-gap-override properties help match the fallback font metrics to the custom font so the swap causes minimal or zero layout shift.

B. Preload Critical Fonts

<link rel="preload" href="/fonts/custom-font.woff2" as="font" type="font/woff2" crossorigin>

Preloading tells the browser to start downloading the font file early, before it encounters the CSS rule. This reduces the window during which the fallback font is displayed.

C. Self-host Your Fonts

Loading fonts from a third-party CDN (like Google Fonts) adds DNS lookup time and connection overhead. Self-hosting your font files on the same domain removes that delay.

D. Use the Font Loading API for Fine Control

document.fonts.load('1em CustomFont').then(() => {
  document.documentElement.classList.add('fonts-loaded');
});

Then in your CSS, only apply the custom font when the class is present:

body {
  font-family: Arial, Helvetica, sans-serif;
}
.fonts-loaded body {
  font-family: 'CustomFont', Arial, Helvetica, sans-serif;
}

Fix #3: Dynamically Injected Content

Why It Causes CLS

Content that gets inserted into the page after the initial render, such as ad banners, cookie consent bars, newsletter signup popups, or promotional banners, pushes existing content down or sideways. This is one of the biggest sources of CLS on real websites.

How to Fix It

A. Reserve Space for Ads and Embeds

If you know the size of your ad slot, wrap it in a container with a minimum height:

<div class="ad-slot" style="min-height:250px;">
  <!-- Ad script loads here -->
</div>

This ensures the browser reserves 250 pixels of vertical space even before the ad script finishes loading.

B. Place Dynamic Content Below the Fold or in Fixed Containers

If possible, inject dynamic content in areas that are not currently visible in the viewport. When content appears below the fold, it does not generate a layout shift because the user cannot see the movement.

C. Use Overlays Instead of Inline Insertions

Cookie consent bars, notification banners, and promotional messages should use fixed or sticky positioning so they overlay the page content rather than pushing it:

.cookie-banner {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  z-index: 1000;
}

Fixed-position elements do not affect the layout of other elements, so they contribute zero CLS.

D. Use CSS contain-intrinsic-size for content-visibility

If you use content-visibility: auto for performance, pair it with contain-intrinsic-size so the browser knows the expected size:

.lazy-section {
  content-visibility: auto;
  contain-intrinsic-size: 0 500px;
}

Fix #4: Late-Loading Third-Party Scripts

Why It Causes CLS

Third-party scripts for analytics, chat widgets, social media embeds, and A/B testing tools often inject DOM elements after the page has rendered. These injected elements can resize containers or push content around.

How to Fix It

  • Audit every third-party script. List all scripts loading on your page. Remove any you no longer need.
  • Defer non-critical scripts. Use the defer or async attribute, or load them after the DOMContentLoaded event.
  • Reserve space for widgets. If a chat widget injects a button in the corner, make sure it uses fixed positioning. If a social embed has a known size, wrap it in a container with explicit dimensions.
  • Use facades. Replace heavy third-party embeds (like YouTube videos) with a static image placeholder that only loads the full embed on user interaction.

YouTube Facade Example

<div class="youtube-facade" style="aspect-ratio:16/9; max-width:560px; cursor:pointer; position:relative;">
  <img src="youtube-thumbnail.jpg" alt="Video title" style="width:100%;height:100%;object-fit:cover;">
  <button aria-label="Play video" style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);">Play</button>
</div>

On click, replace the facade with the actual iframe. This eliminates both the CLS and the initial load cost of the embed.

Fix #5: CSS and JavaScript That Resize Elements After Render

Why It Causes CLS

Sometimes your own code is the problem. JavaScript that measures an element’s height and then applies a style change, or CSS that loads late via a separate stylesheet, can cause visible layout shifts.

How to Fix It

  • Inline critical CSS in the <head> so that above-the-fold layout styles are applied on the first render.
  • Avoid JavaScript that changes layout properties (width, height, top, left, margin, padding) after the page is visible. If you must, use the requestAnimationFrame API or the ResizeObserver to batch changes.
  • Use CSS transforms for animations instead of properties that trigger layout recalculation. For example, use transform: translateY() instead of top or margin-top.

Before (Bad): Animating with layout properties

.notification {
  transition: margin-top 0.3s;
  margin-top: -50px;
}
.notification.visible {
  margin-top: 0;
}

After (Fixed): Animating with transform

.notification {
  transition: transform 0.3s;
  transform: translateY(-50px);
}
.notification.visible {
  transform: translateY(0);
}

The transform-based approach does not trigger a layout recalculation and will not contribute to CLS.

Fix #6: Carousels and Sliders

Why It Causes CLS

Carousels are a common offender because the slider script often initializes after the page loads, rearranging slide elements and changing container dimensions.

How to Fix It

  • Set a fixed height or aspect ratio on the carousel container in CSS.
  • Use overflow: hidden on the container so slides outside the viewport do not affect layout.
  • Load the carousel script early or inline its critical initialization CSS.
.carousel-wrapper {
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  position: relative;
}

A Complete CLS Audit Checklist

Use this checklist to systematically audit any page:

Item What to Check Tool
Images/Videos All have width and height attributes PageSpeed Insights, Lighthouse
Fonts font-display is set; fonts are preloaded or self-hosted DevTools Network tab, Font preview
Ads Ad containers have min-height reserved DevTools Performance panel
Cookie/Notification banners Use fixed or sticky positioning Layout Shift Regions overlay
Third-party scripts Deferred, facades used where possible DevTools Network tab, Coverage tab
CSS animations Using transforms, not layout properties DevTools Performance panel
Carousels/Sliders Container has fixed dimensions and overflow hidden Visual inspection, CLS measurement
Dynamic content injection Space reserved or content placed below the fold DevTools Performance panel

Before and After: Real-World CLS Improvement

Here is a summary of what a typical CLS optimization project looks like:

Page Element Before (CLS Contribution) Fix Applied After (CLS Contribution)
Hero image 0.15 Added width/height + aspect-ratio CSS 0.00
Header ad banner 0.12 Added min-height to ad container 0.00
Web font swap 0.05 Preloaded font + size-adjust fallback 0.01
Cookie consent bar 0.08 Changed to position: fixed 0.00
Total CLS 0.40 0.01

Going from a CLS of 0.40 (poor) to 0.01 (excellent) is absolutely achievable when you address each offending element methodically.

How to Fix Cumulative Layout Shift in WordPress

If your site runs on WordPress, the fixes above still apply, but here are some platform-specific tips:

  • Use a theme that sets image dimensions. Most modern WordPress themes automatically add width and height attributes to images. If yours does not, consider switching or adding a filter in your functions.php.
  • Avoid plugins that inject content above the fold. Popups, notification bars, and slide-in widgets can all cause CLS. Test your site with each plugin enabled and disabled to isolate the problem.
  • Optimize font loading. Plugins like “OMGF” (Optimize My Google Fonts) or “Perfmatters” let you self-host Google Fonts and set font-display properties without editing code.
  • Use a caching and optimization plugin like WP Rocket, LiteSpeed Cache, or FlyingPress that can inline critical CSS, defer non-critical CSS, and delay JavaScript loading.
  • Set explicit sizes for sidebar widgets and ad blocks using the widget’s built-in options or custom CSS.

Monitoring CLS Over Time

Fixing CLS is not a one-time task. New content, theme updates, plugin changes, and third-party script updates can all reintroduce layout shifts. Set up ongoing monitoring using:

  • Google Search Console for field data grouped by URL.
  • PageSpeed Insights API for scheduled automated tests.
  • Real User Monitoring (RUM) using the web-vitals library or a service like DebugBear, SpeedCurve, or Calibre.
  • Lighthouse CI in your deployment pipeline to catch regressions before they go live.

Frequently Asked Questions

What causes a high CLS score?

The most common causes are images without explicit width and height attributes, dynamically injected content like ads or banners, web font loading that causes text reflow, and late-running JavaScript that modifies the DOM after the page is visible.

How do I check my CLS score?

You can check it using PageSpeed Insights for both lab and field data, the Chrome DevTools Performance panel for detailed per-element analysis, or Google Search Console for a site-wide overview of pages with CLS issues.

What is a good CLS score?

Google considers a CLS of 0.1 or less to be good. Scores between 0.1 and 0.25 need improvement, and anything above 0.25 is rated poor.

Does CLS affect SEO?

Yes. CLS is one of Google’s three Core Web Vitals and is used as a ranking signal. Pages with poor CLS may rank lower than competitors with better visual stability, all other factors being equal.

Can I fix CLS without coding?

In many cases, yes. If you use WordPress or a similar CMS, plugins and theme settings can handle image dimensions, font loading, and script deferral. However, some fixes, especially for custom ad placements or injected content, may require editing HTML or CSS.

How long does it take for CLS improvements to show in Search Console?

Google’s CrUX (Chrome User Experience Report) data is collected over a rolling 28-day period. After deploying your fixes, it typically takes 28 days for the field data to fully reflect the improvement. Search Console may take a few additional days beyond that to update its reports.

Do animations always cause CLS?

No. Animations that use CSS transform and opacity properties do not trigger layout recalculation and therefore do not contribute to CLS. Only animations that change layout properties like width, height, margin, padding, top, or left will cause layout shifts.

Wrapping Up

Fixing cumulative layout shift comes down to a simple principle: tell the browser how much space every element needs before it renders. Whether it is an image, an ad slot, a font, or a dynamically injected banner, the fix almost always involves reserving the right amount of space upfront.

Start by running a PageSpeed Insights test, identify the elements contributing to CLS, work through the fixes in this guide, and then set up monitoring to catch any regressions. With a methodical approach, getting your CLS score under 0.1 is well within reach.

Leave a Comment