The glossary

Published:

Last updated:

Cumulative Layout Shift (CLS) – what it measures, thresholds and how to fix layout shifts in Webflow, Next.js and Astro

Definition

Cumulative Layout Shift (CLS) is a web performance metric that shows how much the content on a page moves unexpectedly while it loads. It is one of Google's three Core Web Vitals; a value of 0.1 or lower counts as good.

CLS (Cumulative Layout Shift) is the Core Web Vitals metric that captures visual stability: how much visible content jumps around without the user having done anything. Unlike LCP and INP, CLS does not measure time but how much and how far elements move. A classic example is the button that moves a centimetre just as you are about to click, because an image or a cookie banner has loaded above it. It annoys users, can lower conversion and affects how Google assesses page experience.

What is CLS and how is the score calculated?

CLS is the sum of the scores of all unexpected layout shifts within the page's worst time window – a concept explained below. Each individual shift gets a score according to the formula impact fraction × distance fraction, where both factors are fractions of the viewport. The impact fraction is how much of the visible window is affected by unstable elements – both where they were before the shift and where they ended up after it. The distance fraction is the greatest distance an unstable element moved, divided by the viewport's largest dimension (the height on mobile).

A worked example with hypothetical values: a text section covering 40% of the viewport height is pushed down 10% when an image loads above it. The impact fraction becomes 0.5 (40% original area plus 10% new area) and the distance fraction 0.1. The score for the shift is 0.5 × 0.1 = 0.05. A shift of that size is acceptable on its own, but two in a row take you right to the limit. The formula and definitions are documented on web.dev.

Worth knowing: shifts that happen within 500 milliseconds of a user interaction (click, tap or key press) are not counted. An expandable section opening when someone clicks it is expected and does not affect CLS. Scrolling, however, does not count as an interaction – content that jumps while the user scrolls is counted in full.

Which cumulative layout shift values are good and bad?

Google counts a CLS value of 0.1 or lower as good, 0.1–0.25 as "needs improvement" and over 0.25 as poor. The threshold is assessed at the 75th percentile of page loads – so three out of four visits have to stay within 0.1 for the page to pass. Mobile and desktop are assessed separately. Mobile is almost always the hard part, because narrower screens mean the same element pushes content further down relative to the viewport. The thresholds are documented at web.dev.

Field data (from real visits) is available in the Core Web Vitals report in GSC and in PageSpeed Insights. Lab data from Lighthouse only captures shifts during the initial load, so a page can look clean in the lab but still get a poor CLS score in the field because of content that loads late or moves on scroll.

What is a session window and why does it matter?

A session window is a group of layout shifts that happen close together in time: shifts that occur within 1 second of the previous shift are grouped in the same window, and a window can be at most 5 seconds long. The CLS score for the page is the score of the window with the highest total – not the sum of all shifts during the whole visit. The model was introduced in 2021 so that pages that stay open for a long time (single-page apps, for example) would not be penalised for it. The background is described on web.dev.

What this means for debugging: a single large shift can give the same score as ten small ones close together. If the shifts are spread out with more than a second between them, they end up in different windows and only the worst one counts. So look for the worst cluster of shifts, not just the single largest element.

What are the most common causes of layout shifts?

Most CLS problems come from the browser not knowing how much space something will take before it has loaded. The most common causes are:

  • Images, videos and iframes without dimensions. If width and height (or aspect-ratio in CSS) are missing, no space is reserved, and everything below is pushed down when the file arrives.
  • Web fonts. When a fallback font is swapped for the real one, line height and text width change. You can reduce the effect with font-display, preload and size-adjust on the fallback.
  • Dynamic content without reserved space. Cookie banners from a CMP, notices, campaign banners and late ad slots pushed in above existing content.
  • Animations that change the layout. Animating top, height or margin forces a new layout calculation, while transform moves the element without affecting its surroundings.
  • Content loaded via JavaScript. Client-rendered components, embeds and widgets that fill in the page after the first render.

Google's guide Optimize CLS lists recommended fixes for each cause. Reviewing CLS is usually part of a broader technical SEO audit, alongside LCP and INP.

How do you fix CLS in Webflow, Next.js and Astro?

The causes are the same on every platform, but where you make the fix differs. In Webflow there are three main things to check. First: Interactions that move the layout. Use Move and Scale (which render as transform) instead of Size or Hide/Show on elements that have content below them, because the latter change the document flow. Second: accordions, tabs and sliders need a reserved height or a fixed min-height, otherwise the content below jumps when the component initialises. Third: font loading – avoid adding web fonts only via external custom code that loads late, check font-display and keep the number of font weights down. Also check that images get a fixed aspect ratio in their wrappers, especially in CMS-driven lists where the images vary in size.

In Next.js, the next/image component automatically sets width and height (or requires fill with a sized parent), which in practice removes image CLS as long as you do not bypass the component with a plain <img>. The most common trap is placeholder views, known as skeletons, in loading.tsx: if the placeholder is shorter than the content that replaces it, a shift occurs just as the data arrives. Build the placeholder with the same height and the same number of lines as the real content. See the Next.js documentation.

In Astro, astro:assets does the same job: the <Image> component reads the image's dimensions at build time and outputs width and height for local images, while for images from external sources you need to set them yourself. Because Astro delivers static HTML by default, the risk of late, client-rendered shifts is low – it mainly reappears in islands, the interactive components that fetch data after loading. See the documentation from Astro.

Whatever the platform, add the cookie banner as an overlay (position: fixed) so that it does not push itself into the document flow. Then measure again in the field after each change – the lab does not see everything. You will find more performance checks in Website checklist 2026, and if the page performs well technically but still does not show up, Website not showing on Google? 10 causes is a good next read. At heart, CLS is as much a UX question as an SEO one: a page where nothing jumps feels faster and more reliable, even if its load time has not changed.

Common questions about cumulative layout shift

Do layout shifts caused by the user count?

No. Shifts that happen within 500 milliseconds of a click, a tap or a key press are flagged as expected and are not counted in CLS. A menu that drops down or a section that expands when the user clicks is therefore harmless. Note that scrolling does not count as an interaction – content that jumps while the user scrolls counts in full.

Do cookie banners affect CLS?

Yes, if the banner inserts itself into the document flow and pushes the content below it down. Add the banner as an overlay with position: fixed so that it sits on top of the page instead of moving it. The same goes for notices and campaign banners loaded via a CMP or a tag management system after the first render.

How do I measure CLS in Search Console or CrUX?

In GSC, CLS appears in the Core Web Vitals report, split into mobile and desktop, with groups of pages that pass or fail the 0.1 threshold. The data comes from CrUX (the Chrome User Experience Report), meaning real Chrome visits, and is reported at the 75th percentile. For a single page, PageSpeed Insights gives both field data from CrUX and lab data from Lighthouse. If you want to find exactly which element is shifting, use the Performance panel in Chrome DevTools.

Is fixing images enough to get a good CLS score?

Image dimensions often solve most of it, but rarely all of it. Web fonts, cookie banners, dynamic content and animations that change the layout account for the rest, and on mobile even small shifts have a big effect because the viewport is small. Fix the images first, measure again in the field and then move on to the next cause in the worst time window.

Ready for the next step?

Now that you know more about CLS, you may be wondering what your next step should be.

We are always happy to help and to answer any questions you have.

Book an intro call