How to Fix Cumulative Layout Shift (CLS) Issues on Your Website

Why Fixing CLS Issues Should Be Your Top Priority in 2026

Cumulative Layout Shift (CLS) measures how much your page content moves around unexpectedly while it loads. A high CLS score frustrates users, tanks your Core Web Vitals, and directly hurts your Google rankings.

If you have been staring at Search Console warnings that say “CLS issue: more than 0.1” and you are not sure where to start, this guide is for you. We will walk through every common cause of high CLS, show you exactly what the broken code looks like, and give you the fixed version you can copy into your project today.

A good CLS score is 0.1 or less. Anything above 0.25 is considered poor. Our goal is to get you well under that 0.1 threshold.

Quick Reference: Common CLS Causes and Fixes

Before we dive deep, here is a summary table of the most frequent culprits behind high CLS scores and the recommended fix for each one.

CLS Cause Why It Shifts Layout Fix
Images without dimensions Browser cannot reserve space before image loads Set explicit width and height attributes
Dynamic ad slots Ads load late and push content down Reserve a fixed-size container with min-height
Web fonts (FOUT/FOIT) Fallback font swaps to custom font causing reflow Use font-display: optional or size-adjust
Injected content (banners, popups) Elements inserted into the DOM push existing content Use overlays or pre-allocate space
Iframes and embeds without dimensions Same as images: no reserved space Wrap in an aspect-ratio container
Late-loading CSS or JS Styles applied after render cause reflow Inline critical CSS, defer non-critical JS
BFCache ineligibility Back/forward navigation triggers full reload Remove unload listeners, audit Cache-Control

Now let us break each one down with real code.

1. Fix CLS Issues Caused by Images Without Dimensions

This is the single most common reason for a high CLS score. When you include an <img> tag without width and height attributes, the browser allocates zero space for the image. The moment the image file arrives, everything below it jumps down.

Before (Bad)

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

The browser has no idea this image is 1200×600 pixels until it downloads the file. Until then, the element has 0 height.

After (Fixed)

<img src="/hero-banner.webp" alt="Hero banner" width="1200" height="600" loading="lazy">

By setting width and height, the browser calculates the correct aspect ratio and reserves the right amount of vertical space before the image loads. You can still control the rendered size with CSS:

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

This combination gives you responsive sizing and zero layout shift. Modern browsers use the HTML attributes purely to compute the aspect ratio when height: auto is present.

Using the CSS aspect-ratio Property

If you prefer a CSS-only approach (useful for background images or when HTML attributes are not practical):

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

2. Fix CLS Issues Caused by Dynamic Ads

Ads are one of the trickiest CLS offenders because you often do not control the ad creative size, and ad scripts load asynchronously. The ad slot starts at 0 height, then expands to 250px or 300px once the creative arrives.

Before (Bad)

<div id="ad-slot-top">
  <!-- Ad script loads here dynamically -->
</div>

After (Fixed)

<div id="ad-slot-top" style="min-height:250px; background:#f0f0f0;">
  <!-- Ad script loads here dynamically -->
</div>

Key principles for ad-related CLS fixes:

  • Always set a min-height on the ad container that matches the most common ad size for that slot.
  • If the ad might not fill (unfilled inventory), use a collapse strategy that does not shift content. One approach is to keep the reserved space and show a subtle background or house ad instead of collapsing to zero.
  • If you use Google AdSense or Ad Manager, check if the slot supports size reservation via the ad tag configuration. In GPT (Google Publisher Tag), you can use googletag.pubads().setCentering(true) and define fixed slot sizes.
  • For sticky or anchor ads, prefer overlay positioning (position: fixed) so they do not push inline content.

Multi-Size Ad Slots

If a single slot can serve 300×250 or 300×600, reserve the larger size and center the smaller creative within it. The tradeoff is some empty space, but zero layout shift.

.ad-container-multi {
  min-height: 600px;
  display: flex;
  align-items: center;
  justify-content: center;
}

3. Fix CLS Issues Caused by Web Fonts

When a custom web font loads, the browser often swaps from a fallback system font to the new font. Because the two fonts have different metrics (character width, line height, ascender/descender), the swap causes text to reflow and nearby elements to shift.

Understanding Font Loading Behaviors

font-display Value Behavior CLS Impact
swap Shows fallback immediately, swaps when font loads High (visible reflow)
block Invisible text for up to 3 seconds, then swaps Medium (still shifts on swap)
optional Uses font only if already cached; otherwise sticks with fallback None
fallback Short block period, short swap period Low

Recommended Fix: Use font-display: optional

@font-face {
  font-family: 'BrandFont';
  src: url('/fonts/brandfont.woff2') format('woff2');
  font-display: optional;
}

With optional, the browser uses the custom font only if it is already in the cache. On the first visit the user sees the system fallback. On subsequent visits the cached font loads instantly with no swap and no layout shift.

Advanced: Use size-adjust for Fallback Matching

If font-display: optional is too aggressive for your brand requirements, you can minimize the reflow by making the fallback font metrically similar:

@font-face {
  font-family: 'BrandFont Fallback';
  src: local('Arial');
  size-adjust: 105%;
  ascent-override: 92%;
  descent-override: 22%;
  line-gap-override: 0%;
}

body {
  font-family: 'BrandFont', 'BrandFont Fallback', sans-serif;
}

Tools like Fontaine or the Next.js @next/font module can auto-generate these override values for you.

Preload Critical Fonts

Reduce the time to font availability by preloading:

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

This tells the browser to start downloading the font file early, before the CSS parser even discovers the @font-face rule.

4. Fix CLS Issues From Injected Content

Cookie consent banners, newsletter popups, notification bars, and dynamically injected promotional banners are all common sources of layout shift when they push page content instead of overlaying it.

Before (Bad)

<!-- Cookie banner inserted at top of body by JS -->
<div class="cookie-banner">
  We use cookies...
</div>
<header>...</header>
<main>...</main>

When this div gets injected after initial render, everything below it shifts down.

After (Fixed): Overlay Approach

<div class="cookie-banner" style="position:fixed; bottom:0; left:0; right:0; z-index:9999;">
  We use cookies...
</div>

By using position: fixed (or position: sticky at the top), the banner does not affect the document flow and causes zero CLS.

After (Fixed): Pre-allocated Space Approach

If the banner must be inline (not overlaid), reserve the space in HTML from the start:

<div class="banner-placeholder" style="min-height:60px;">
  <!-- JS will populate this -->
</div>
<header>...</header>

This way the space exists from the first paint. When the JavaScript fills it, nothing moves.

5. Fix CLS Issues From Iframes and Embeds

YouTube videos, Google Maps, social media embeds, and third-party widgets loaded via <iframe> behave just like images without dimensions: they start at 0 height and expand once loaded.

Before (Bad)

<iframe src="https://www.youtube.com/embed/abc123" allowfullscreen></iframe>

After (Fixed)

<div style="aspect-ratio:16/9; width:100%;">
  <iframe src="https://www.youtube.com/embed/abc123" 
    allowfullscreen 
    style="width:100%; height:100%; border:0;"
    loading="lazy">
  </iframe>
</div>

The wrapper div with aspect-ratio: 16/9 reserves the exact space the video needs. No shift.

6. Fix CLS Issues From Late-Loading CSS and JavaScript

If a stylesheet or script loads after the initial paint and changes the layout (for example, a slider plugin that recalculates widths, or a CSS file that changes padding), it will cause CLS.

Steps to Fix

  1. Inline critical CSS in the <head> so above-the-fold content is styled on first render.
  2. Defer non-critical JS using the defer attribute or load it at the bottom of the body.
  3. Avoid JavaScript that modifies layout after load. If you must, use requestAnimationFrame or the content-visibility CSS property to batch changes.
  4. Audit third-party scripts. Tag managers, analytics, chat widgets, and A/B testing tools can inject styles and DOM elements that cause shifts. Load them asynchronously and check their CLS impact individually.

Identifying the Offending Script

Use Chrome DevTools Performance panel:

  1. Open DevTools and go to the Performance tab.
  2. Check “Layout Shifts” in the settings.
  3. Record a page load.
  4. Click on any layout shift event in the timeline to see exactly which DOM nodes moved and what triggered the shift.

This is the fastest way to pinpoint which script or style change is responsible.

7. Enable Back/Forward Cache (BFCache)

When a user navigates back or forward, the browser can restore the page from memory instantly with zero layout shift. This is called BFCache. Pages that are not eligible for BFCache trigger a full reload, which means all your CLS-causing elements shift again.

Common BFCache Blockers

  • Using the unload event listener (use pagehide instead)
  • Setting Cache-Control: no-store on the main document
  • Having open WebSocket or WebRTC connections during navigation

How to Test BFCache Eligibility

  1. Open Chrome DevTools.
  2. Go to Application tab.
  3. Click Back/forward cache in the sidebar.
  4. Click “Test back/forward cache”.
  5. Review any listed blockers and fix them.

8. Step-by-Step CLS Debugging Workflow

Here is a practical workflow you can follow every time you need to fix CLS issues on a site:

  1. Measure first. Run a PageSpeed Insights test and note the CLS score for both mobile and desktop. Check Search Console Core Web Vitals report for field data.
  2. Identify the shifting elements. Use Chrome DevTools Performance panel or the Web Vitals extension to highlight which elements shift.
  3. Categorize the cause. Match each shift to the categories in this guide: images, ads, fonts, injected content, iframes, or late CSS/JS.
  4. Apply the fix on a staging environment. Never push CLS fixes directly to production without testing.
  5. Re-measure. Run PageSpeed Insights again on your staging URL. Confirm CLS is below 0.1.
  6. Deploy and monitor. After going live, watch the Search Console Core Web Vitals report for 28 days to confirm field data improves.

9. CLS Fixes for Popular Platforms

WordPress

  • Use a theme that outputs width and height on all images (WordPress core has done this since version 5.5, but some themes strip it).
  • If you use a page builder like Elementor or Divi, check slider and animation widgets for missing dimension settings.
  • Disable plugins one by one on a staging site to isolate which one causes CLS. Common offenders: cookie consent plugins, lazy load plugins that remove native width/height, and popup plugins.

Shopify

  • Edit your Liquid templates to include width and height attributes on <img> tags, especially product images and hero banners.
  • For dynamic sections loaded via Shopify’s section rendering API, pre-allocate container heights.
  • Use Shopify’s built-in image_tag filter which can automatically output dimensions.

Squarespace, Wix, and Other Builders

  • Your control over HTML output is limited. Focus on what you can control: font choices (use system fonts or preloaded fonts), image aspect ratios set in the platform’s editor, and minimizing third-party scripts.
  • Use Custom CSS injection to add aspect-ratio or min-height to known problem sections.

10. Tools to Measure and Monitor CLS

Tool Type Best For
Google Search Console Field data (real users) Monitoring CLS across your entire site over time
PageSpeed Insights Lab + field data Quick per-URL diagnosis with actionable suggestions
Chrome DevTools (Performance tab) Lab data Pinpointing exactly which elements shift and when
Web Vitals Chrome Extension Real-time overlay Quick visual check while browsing your site
DebugBear Synthetic monitoring Continuous automated testing with historical trends
Lighthouse CI CI/CD integration Catching CLS regressions before deployment

Checklist: The Complete CLS Fix Checklist

Use this checklist to audit any page:

  • ☐ All <img> tags have width and height attributes
  • ☐ All <video> and <iframe> elements have explicit dimensions or an aspect-ratio wrapper
  • ☐ Ad slots have min-height matching expected ad sizes
  • ☐ Web fonts use font-display: optional or fallback fonts with size-adjust
  • ☐ Critical fonts are preloaded
  • ☐ Cookie banners and notification bars use position: fixed or sticky
  • ☐ No JavaScript injects DOM elements that push content after initial render
  • ☐ Critical CSS is inlined in <head>
  • ☐ Third-party scripts are loaded asynchronously
  • ☐ Page is eligible for BFCache (no unload listeners, no no-store on main document)
  • ☐ CLS tested on both mobile and desktop
  • ☐ CLS score confirmed below 0.1 in PageSpeed Insights

Frequently Asked Questions

What is a good CLS score?

Google considers a CLS score of 0.1 or less as good. Scores between 0.1 and 0.25 need improvement. Anything above 0.25 is rated poor and will negatively impact your Core Web Vitals assessment.

How do I find what is causing CLS on my site?

The fastest method is to open Chrome DevTools, go to the Performance tab, record a page load, and look for the “Layout Shift” markers in the timeline. Clicking on each one shows you the exact DOM node that moved and by how many pixels.

Can CLS affect my Google rankings?

Yes. CLS is one of the three Core Web Vitals metrics that Google uses as a ranking signal. A poor CLS score can hurt your visibility in search results, especially on mobile.

Why is my CLS score different on mobile vs. desktop?

Mobile screens are narrower, so the same layout shift covers a larger proportion of the viewport. Ads, images, and font swaps that barely register on desktop can cause significant CLS on mobile. Always test and fix CLS issues for mobile first.

Does lazy loading images cause CLS?

Not if done correctly. Native lazy loading (loading="lazy") combined with explicit width and height attributes does not cause CLS because the browser still reserves the correct space. Some JavaScript-based lazy loading libraries, however, can strip dimensions or use placeholder elements with incorrect sizes, which causes shifts.

How long does it take for Google to recognize CLS fixes?

Google’s Core Web Vitals report in Search Console uses a 28-day rolling average of real user data. After deploying your fixes, expect it to take approximately 28 days before the report shows improvement. You can validate fixes immediately using PageSpeed Insights lab data.

Do I need to fix CLS on every single page?

Focus on page templates rather than individual URLs. If your blog post template has a CLS issue, fixing the template fixes every blog post at once. Search Console groups URLs by issue type, which helps you prioritize templates that affect the most pages.

Need Help Fixing CLS Issues on Your Site?

At Wicked SEO, we specialize in Core Web Vitals optimization and technical SEO audits. If you have tried the fixes above and still cannot get your CLS score under control, or if you want a professional audit of your entire site, get in touch with our team. We will identify every layout shift, fix it at the code level, and monitor your scores until they are solidly in the green.