SEO check

Cumulative Layout Shift (CLS): Score, Causes and Fixes

What CLS measures, how the 0.1 and 0.25 limits work, and how our free report tests your page for layout jumps.

Free to use 19 min read Updated Oct 2026

What is cumulative layout shift? The quick answer

Quick answer: Cumulative layout shift (CLS) is the Core Web Vitals score for how much visible content jumps around unexpectedly while a page loads. A score of 0 means nothing moved, and a bigger number means more movement. Our check wants a CLS of 0.1 or less.

  • Pass: a CLS of 0.1 or less. Exactly 0.1 still passes.
  • Fail: over 0.25. Anything above 0.1 up to 0.25 is a warning, which our report does not count as a pass.
  • Fix: reserve space for every image, ad and embed, load fonts without a size change, and never push content down after it has rendered.

Check my site free

Our free report loads your page in a real headless browser and totals every unexpected shift up to the end of page load. If you landed here from an amber or red row, the fix steps are further down the page.

What does cumulative layout shift actually measure?

You reach for a link, a banner loads above it, the text drops and you tap the wrong thing. That small annoyance is exactly what this metric was built to catch. A layout shift happens when a visible element changes position between two rendered frames without the visitor asking for it. CLS combines those shifts into one unitless score, so it is not seconds or milliseconds. The specification calls the underlying behaviour layout instability. The measurement comes from the Layout Instability API, a WICG draft built into Chromium based browsers.

CLS is one of three Core Web Vitals. Largest Contentful Paint covers loading, Interaction to Next Paint covers responsiveness, and CLS covers visual stability. Our Core Web Vitals guide walks through all three together.

You will see the number in Lighthouse, PageSpeed Insights, Chrome DevTools, the Core Web Vitals report in Search Console and the Chrome User Experience Report. The metric is about surprise. Content added below the viewport, or a change that follows your own click, is not the target.

Layout shift versus cumulative layout shift

One layout shift is a single movement with its own score. CLS is the total for the page, taken the way Google defines it, which the next section explains.

Expected and unexpected shifts

Shifts fall into two groups: ones the visitor caused and ones they did not. A shift that starts within 500 milliseconds of a tap, click or key press is flagged as caused by input and ignored. Opening an accordion or expanding a menu are fair examples that do not count. An ad that loads late or a font that swaps in does count, because nobody asked for either.

How is cumulative layout shift calculated?

Each layout shift gets a score, and the score is the impact fraction multiplied by the distance fraction. The impact fraction is the share of the viewport covered by the unstable elements across both frames, so the areas they filled before and after the move are counted together. The distance fraction is the greatest distance any unstable element travelled, divided by the larger viewport dimension, whichever of width or height is bigger.

The formula: layout shift score = impact fraction x distance fraction. The score has no unit, and a value of 0.1 sits at the edge of the good range.

Take a phone viewport of 390 by 844 pixels. A full width block starts at the very top and fills 60 percent of the height. A late banner pushes it down by 20 percent of the height. The block's old and new positions together cover 80 percent of the viewport, so the impact fraction is 0.8. The distance fraction is 0.2, and the shift scores 0.16. That is already in the needs improvement band.

Run the same layout in a 1280 by 720 desktop window and the width becomes the larger dimension. The block still covers 80 percent, but a 144 pixel move now counts as 144 divided by 1280, which is 0.1125. The score drops to about 0.09. This is one reason the same page often scores worse on a phone than on a desktop.

Google publishes the full definition of the score, the thresholds and the windowing rules in Google's CLS definition, written by the team that defined the metric.

Impact fraction and distance fraction

The table below sets the two examples above beside a smaller case: the same block dropping only 10 percent of the height. Its old and new positions cover 70 percent of the viewport and the move is a tenth of the height, so it scores 0.07 and stays in the good range. Because the fractions multiply, a large block that moves a little and a small button that leaps far can score alike: a button whose old and new positions together cover 8 percent, moving 90 percent of the height, also scores about 0.07.

ViewportImpact fractionDistance fractionShift score
Phone, 390 by 844, 20 percent drop0.80.20.16
Desktop, 1280 by 720, 144 pixel drop0.80.1125About 0.09
Phone, 390 by 844, 10 percent drop0.70.10.07

Why Google uses session windows

A long lived single page app or an infinite scroll feed would keep adding tiny shifts forever, so a lifetime total would punish any page people stay on. Google groups shifts into bursts instead. A burst ends after 1 second with no shift, or after 5 seconds in total, and the worst burst, called the maximum session window, becomes the page's CLS. Our report totals all shifts rather than the worst burst, a difference we cover in the testing section below.

What is a good cumulative layout shift score?

Google's bands are fixed, and they apply to the 75th percentile of real visits, with mobile and desktop reported separately. One test run is therefore a hint, not a verdict about your whole audience.

RatingCLS scoreWhat visitors feel
Good0.1 or lessA steady page where nothing moves under the finger
Needs improvementAbove 0.1 up to 0.25Noticeable nudges while reading or tapping
PoorAbove 0.25Content visibly jumping as the page loads

Our report uses the same two cut offs on a single lab load: pass at 0.1 or less, warning above 0.1 up to 0.25, failed above 0.25. The details are in the testing section below. A score of 0 is common on simple pages. Aim to stay at 0.1 or below, and do not strip out useful features such as ads just to chase a perfect zero.

Why does cumulative layout shift matter for users and SEO?

The user cost comes first. People mis-tap links and checkout buttons, lose their reading position, and watch forms move while they type. It hurts most on phones, where one shift can slide a button out from under the thumb.

Site owners pay as well. Accidental ad clicks tend to be low quality, and visitors who feel a page is unreliable are more likely to leave. Treat those as likelihoods, not guarantees.

On the search side, Google lists CLS as one of its three Core Web Vitals and says they line up with what its ranking systems reward. Google Search Central guidance sets out the 0.1 target, and Search Console groups your URLs as good, needs improvement or poor. Be straight about the weight, though: relevance and great content still lead, and CLS is one signal among many. Treat it as a quality bar that also protects conversions.

The loading vital sits next to this one in the report, so read the Largest Contentful Paint check too.

What causes cumulative layout shift?

Almost every shift is one of two things: something changed size after the browser placed it, or something new was inserted above content that was already visible. The six causes below match the six fixes later on, so you can jump across once you recognise your page. One template usually repeats the same cause on every page, so fixing the template fixes the site.

Images, video and iframes without dimensions

With missing width and height attributes, the browser reserves nothing, then reflows the page when the file arrives. A hero image, a product photo and a YouTube embed are the usual offenders.

Ads, embeds and third party widgets

Ads without reserved space are a leading source of shift. Ad slots often start empty and grow when a creative arrives, or collapse when no ad fills. Social embeds, review widgets and chat bubbles can resize themselves after loading. A 300 by 250 slot that opens at zero height pushes everything below it down by 250 pixels.

Content inserted above what is already on screen

Cookie bars, promo strips and signup popups that push the page down are classic cases, and the Layout Shift Regions view makes a cookie banner shift easy to spot. Late injected content does the same thing, meaning blocks rendered by scripts after the first paint. When a script is slow, the insertion lands late and the damage is bigger, so see the JavaScript execution time guide.

Web fonts that swap in at a different size

A web font swap is hard to spot because it looks like polish. The fallback font and the final font rarely share the same widths and line heights, so paragraphs rewrap when the real font lands. Designers call the visible swap a flash of unstyled text, and the blank stretch before it a flash of invisible text.

Animations that change layout

Animating top, left, height or margin shifts the element or its neighbours on every frame. Transform and opacity do not trigger layout, which is why they are the safe pair. Compare transition: height 0.3s, which moves everything below it, with transform: translateY(-100%), which moves only the element itself. Lighthouse flags animations that cannot run on the compositor as non-composited animations.

Late styles and lazy loaded content

A stylesheet that arrives after first paint restyles content already on screen, and a lazy loaded block with no reserved height pops in and pushes its neighbours. A typical case is a Google Fonts stylesheet that lands late, so visible text suddenly changes size or spacing. The render blocking resources guide covers how to load styles so they arrive before the first paint.

How SEO Site Checker tests cumulative layout shift

The main report does not call the Lighthouse API for this row, even though Lighthouse reports the same metric as its own CLS audit. It opens your URL in a headless Chromium browser driven by Playwright and measures layout shift itself. What follows describes that check exactly, including what it leaves out.

The exact rule: CLS is the sum of every layout shift not flagged as caused by recent input, collected until the page load event (20 second limit), rounded to four decimals. Passed at 0.1 or less, warning above 0.1 up to and including 0.25, failed above 0.25.

What our check measures

Before the page starts loading, the browser registers a PerformanceObserver for the layout-shift entry type with buffering switched on. Every entry carries a value and a hadRecentInput flag, both documented in the LayoutShift API reference. When that flag is false, the entry's value is added to a running total. Shortly after the load event fires, the report reads the total and rounds it. A page where nothing shifted shows 0. If the page cannot be loaded at all, the report returns an error and there is no CLS row.

The browser runs one desktop sized window of roughly 1280 by 720. It does not scroll, click, throttle the network or CPU, or emulate a phone.

Pass, warning and failed in our report

  • Pass: 0.1 or less. You get a green tick and the score. Exactly 0.1 passes.
  • Warning: above 0.1 up to and including 0.25, so exactly 0.25 is still a warning. The row shows an amber icon, the score, the 0.1 target, a How to Fix button and a link to this guide, and the alert box and button are red even though the icon is amber.
  • Fail: above 0.25. The same score, target, button and guide link appear, with a red icon.

The overall report score is the share of checks that passed, so a warning counts as not passed.

Why your score may differ from PageSpeed Insights

  • Different totals. Our figure adds every shift up to load, while Google reports only the worst session window. For the same load ours is equal or higher, but the PageSpeed Insights mobile run also emulates a phone, so it can still come out above ours.
  • Timing. A chat widget that opens a few seconds after the load event, or a banner that appears once you scroll, shifts the page after our reading is taken, so only field data will show it.
  • Frames. Our check reads the top-level page only, so shifts that happen inside ad or embed iframes are not counted, even when they are plainly visible to a visitor. If the ad frame itself grows and pushes your own content down, that movement is counted; only shifts inside the frame are missed.
  • Devices. Real visitors on slower phones often see more shifts than one lab run does, because late resources arrive in a different order on a weak connection and a slow processor.
  • Ad variation. Ads change between loads, and a taller creative on one visit can double the score of the next, so run the check twice before you trust one number.

A pass here shows that your load looked steady in one desktop sized window. It does not prove that real phone visitors get the same experience. When you are ready, run the free report and compare the row with your field data.

How do you check cumulative layout shift?

No single tool tells the whole story, so use two or three together.

  1. Run the free report. Open our free report and read the Cumulative Layout Shift Test row for the score and its rating.
  2. Open PageSpeed Insights. Compare the real user data (CrUX field data, at the 75th percentile) with the lab run, since lab data versus field data is the first thing to separate. If the real user number is higher, shifts may be happening after load or on slower phones than the lab run assumes.
  3. Record a load in Chrome DevTools. In the Performance panel, open the Layout Shifts track. Then tick Layout Shift Regions in the Rendering tab (open DevTools, then the three-dot menu, More tools, Rendering) to see moving areas flash.
  4. Use Search Console for the whole site. Its Core Web Vitals report groups URLs by status, though it needs enough real visitor data to appear.
  5. Log shifts from the console. Developers can paste a short observer snippet to print each shift with its source elements, which the browser exposes as LayoutShiftAttribution objects. For production monitoring, the web-vitals library exposes onCLS(), and its attribution build also reports the element that moved most. The library also reports a page restored from the back/forward cache as a fresh measurement, something a single lab load like ours never exercises.

The snippet below mirrors what our check does, except that it logs the culprits instead of a total. Paste it into the Console of the loaded page and press Enter. Because of buffered: true it prints the shifts that already happened. A reload wipes the observer, so paste it again (or save it under Sources, Snippets) after any reload. To see the same shifts on a timeline, record a reload in the Performance panel as a separate step.

JavaScript
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.hadRecentInput) continue;
    console.log(entry.value.toFixed(4), entry.sources.map((s) => s.node));
  }
}).observe({ type: 'layout-shift', buffered: true });

How to fix cumulative layout shift step by step

The order below follows impact, with the biggest movers first. Fix the template once instead of patching page by page, and reload after each change so you know which step helped.

Give every image, video and iframe a size

If the symptom is text dropping when a picture appears, add width and height attributes in pixels. The browser then reserves the box from the aspect ratio before the file arrives. Keep CSS on responsive images so they still scale. The attributes only need to carry the right aspect ratio, but exporting at the displayed size with the Image Resizer keeps the file light and the numbers honest.

HTML
<img src="hero.jpg" width="800" height="450" alt="Product hero">
CSS
img {
  max-width: 100%;
  height: auto;
}

Reserve space for ads, embeds and late widgets

Wrap each slot in a container with a fixed min-height or an aspect-ratio that matches the tallest creative you serve. Keep the space when no ad fills, because collapsing it shifts the page too.

CSS
.ad-slot { min-height: 250px; }
.embed { aspect-ratio: 16 / 9; width: 100%; }

Never push content down after it has rendered

Cookie bars and promo strips should overlay the page with position fixed, or sit in a slot reserved from the first paint. Insert new content below the viewport or in response to a tap, never above what the visitor is already reading. A fixed bar overlays the page instead of reflowing it.

CSS
.cookie-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
}

Load web fonts without a size jump

Preload the one or two key font files and set font-display. The preload tag needs the crossorigin attribute even for files on your own domain, otherwise the browser downloads the font twice and the preload is wasted. Then tune a local fallback with size-adjust, ascent-override and descent-override so both faces take the same space. The local() name must be a font that exists on the visitor's device: Arial covers Windows and Apple devices, while many Android phones ship Roboto instead. Their metrics differ, so define one fallback face per base font, measure each against your web font, and list both in the font-family stack. The value font-display: optional is the zero shift choice, since the browser keeps the fallback if the font is late.

HTML
<link rel="preload" href="/fonts/brand.woff2" as="font" type="font/woff2" crossorigin>
CSS
@font-face {
  font-family: "Brand";
  src: url("/fonts/brand.woff2") format("woff2");
  font-display: swap;
}
@font-face {
  font-family: "Brand Fallback Arial";
  src: local("Arial");
  size-adjust: 104%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Brand Fallback Roboto";
  src: local("Roboto");
  size-adjust: 101%;
  ascent-override: 95%;
  descent-override: 25%;
  line-gap-override: 0%;
}
body { font-family: "Brand", "Brand Fallback Arial", "Brand Fallback Roboto", sans-serif; }

The percentages above are placeholders, so measure your own font against each fallback before copying any number. Browsers that do not support the override descriptors ignore them, so the rule is safe to ship everywhere.

Animate with transform and opacity

Swap height, top, margin and left animations for transform and opacity. A banner that slides in with translateY costs no layout shift. The same banner animating its height pushes everything beneath it down on every frame, and each of those frames can add to the score.

Use placeholders for lazy and script rendered content

Give lazy loaded blocks and client rendered components a skeleton with the final height. If you use content-visibility: auto on long sections below the fold, pair it with contain-intrinsic-size so each skipped section keeps a realistic height and the scrollbar does not jump.

How to reduce cumulative layout shift in WordPress, Shopify and other platforms

Each platform has its own usual suspects, so start with the row that matches your site.

PlatformWhere shifts usually come fromWhere to fix it
WordPressPage builder images, ad and popup plugins, theme fontsTheme templates and plugin settings
ShopifyApps that load late, review and chat widgets, images without widthLiquid image_tag filter, fixed containers for app blocks
Wix and SquarespaceEmbeds, third party apps, custom fontsFixed height embed blocks, installed apps list
Next.js or ReactImages without size, client rendered components, font loadingImage width and height props, skeletons, the framework font helper
Plain HTMLAny image, ad or font in the templateThe template itself, using the steps above

On WordPress, core already adds width and height to images inserted through the editor, so shifts usually come from images hard coded in the theme or a page builder, from ad and popup plugins, and from theme fonts. Check the theme template first, then disable plugins one at a time.

On Shopify, the Liquid filter chain {{ image | image_url: width: 800 | image_tag }} outputs width and height from the real image, so swap hand written img tags for it. Give app blocks, review widgets and chat launchers a fixed height container.

On Wix and Squarespace, you usually can't edit the core template files, so find the offender first: record a load in the Performance panel or switch on Layout Shift Regions to see which block moves, then give that embed a fixed height or remove the app that injects it. On Next.js, pass width and height (or fill inside a sized parent) to the image component and load fonts through the built-in font helper, which also generates a matched fallback.

Cumulative layout shift best practices checklist

  • Size every media element. Each img, video and iframe has width and height or an aspect-ratio.
  • Reserve ad and embed space. Every slot sits in a container with reserved height.
  • Keep the top stable. Nothing is inserted above existing content after first paint.
  • Match your fonts. Key fonts are preloaded and fallback metrics are matched.
  • Animate safely. Animations use transform and opacity only.
  • Add skeletons. Lazy loaded and script rendered blocks have placeholders of the final height.
  • Test more than once. Check mobile and desktop layouts, and run each test twice.

Common mistakes when fixing layout shift

Five slips make people think a fix did not work, and each one has a recognisable symptom.

  • Checking only the lab score. Shifts that happen after load, on scroll or on slower phones only show up in the PageSpeed Insights field data or in Search Console, so a clean lab run can still sit beside a poor field number.
  • Forgetting height: auto. Width and height attributes without that CSS rule stretch or squash responsive images, which looks like a new bug right after you fixed the old one.
  • Collapsing an empty ad slot. When no ad fills and the container shrinks to nothing, everything below it moves up, and that is a layout shift of its own.
  • Relying on font-display: swap alone. Without matched fallback metrics the text still rewraps the moment the real font lands, so the score barely changes.
  • Calling it done after one desktop run. Real visits across mobile and desktop decide the verdict, so compare your before and after runs from the same viewport size, then repeat the measurement in Chrome DevTools with the device toolbar set to a phone, since our report only loads a desktop sized window.

How to Fix

The short version, the same steps you see when you click How to Fix in your report.

Why this fails: Parts of your page moved after they were first drawn. The report loads the page in a browser and adds up every shift that was not caused by a click, tap or key press until loading finishes. A total of 0.1 or less passes, up to 0.25 is a warning, and above 0.25 fails.

How to fix it:

  1. Size your media. Add width and height to every img, video and iframe, and keep CSS height: auto so they stay responsive.
  2. Reserve space. Give ads, embeds and widgets a container with min-height or aspect-ratio, and keep it when no ad fills.
  3. Stop pushing content. Overlay banners and cookie bars with position: fixed, or reserve their slot from the first paint.
  4. Match your fonts. Preload the main font and set font-display, ideally with a fallback tuned using size-adjust.
  5. Animate safely. Use transform and opacity, not top, height or margin.
HTML
<img src="hero.jpg" width="800" height="450" alt="Hero">
<style>
  img { max-width: 100%; height: auto; }
</style>
CSS
.ad-slot { min-height: 250px; }
.embed { aspect-ratio: 16 / 9; }
  • WordPress: check page builder images, ad and popup plugins and theme font settings.
  • Shopify: use the image_url and image_tag filters with a width, and give app blocks a fixed height.
  • Wix and Squarespace: use fixed height embed blocks and review installed apps.
  • Next.js or React: pass width and height props to images and use skeleton components.
  • Plain HTML: apply the steps above in your template.

Re-test: In Chrome DevTools, tick Layout Shift Regions in the Rendering tab and reload to see what still moves. Then run the free SEO check again and look for 0.1 or less. Test twice, since ads vary.

Re-test my website

Frequently Asked Questions

What is a good cumulative layout shift score?
A CLS of 0.1 or less is good, above 0.1 up to 0.25 needs improvement, and above 0.25 is poor. Google judges the 75th percentile of real visits, and reports mobile and desktop separately. Our report applies the same cut offs to a single lab load of your page. A score of 0 is common on simple pages, so you only need to stay at or under 0.1.
Does cumulative layout shift affect SEO?
Yes, because CLS is one of the three Core Web Vitals, which Google says align with what its ranking systems reward. Keep it in proportion, though. It is one signal among many, content relevance still leads, and a page that jumps around also costs you conversions and trust.
What causes cumulative layout shift?
The main causes are images and embeds without dimensions, ads that load late, content injected above what is already visible, web font swaps and animations that change layout. Late styles and lazy loaded blocks add to the list. The causes section above gives an example of each one.
How do I fix cumulative layout shift?
Start with the biggest movers. Give images and embeds a size, reserve space for ads, stop banners pushing content down and match your fallback fonts to the real ones. Then run the check again and confirm the score is 0.1 or less.
How do I check my cumulative layout shift score?
Run the free report for a quick lab score, then confirm it with PageSpeed Insights field data and the Layout Shifts track in Chrome DevTools. Search Console covers the whole site once enough real visitor data exists, so it suits larger sites best.
Do clicks and taps count toward cumulative layout shift?
Only discrete actions count as input: a shift that begins within 500 milliseconds of a tap, click or key press is flagged as user initiated and left out of the score. A shift that follows a late ad or a font swap still counts, because the visitor did not trigger it. Scrolling is not treated as input.
Is cumulative layout shift only measured on the first page load?
No. Google's field measurement runs across the whole visit, so shifts that happen after load count there. Lighthouse and our check only see the load, which is why real user data can come out higher than a lab result for the same page.
Why is my CLS different in PageSpeed Insights and in a lab test?
Field data comes from real visitors at the 75th percentile, while a lab run is one load on one device. Our total also adds every shift up to load, where Google keeps the worst burst. The PageSpeed Insights mobile tab also emulates a throttled phone, which our desktop sized check does not.
How do I fix cumulative layout shift in WordPress?
Find the images, ad slots, popups and fonts that load late. Set sizes in the theme or page builder, reserve ad space with a fixed height, and match fallback fonts. Clear any caching plugin, then run the check again to confirm the new score.

See how your site scores right now

Paste your address into the free report, then use the How to Fix button on anything that needs work.

Free to use. Results in under a minute for most sites.