Cumulative Layout Shift (CLS): how to measure and fix layout shifts on your website
Table of contents
You were about to hit "Buy", the content moved and you clicked something else. That is the Cumulative Layout Shift (CLS): the visual stability metric of the Core Web Vitals, and it almost always has a concrete cause you can remove.
What the Cumulative Layout Shift (CLS) Measures: Visual Stability, Not Speed
CLS quantifies how often users suffer unexpected content movement. Unlike LCP (seconds) or INP (milliseconds), it is a unitless score: it sums up how much the content the user was reading moved, and how far it travelled.
Not every shift is a problem. If content moves because the user clicked or pressed a key, the browser treats it as expected and does not penalize it. What counts is the movement nobody asked for, and its real damage is documented in the official CLS documentation: a shift can make someone confirm an order they wanted to cancel. If you want the big picture of all three metrics, start with what the Core Web Vitals are.
A Three-Step Cause: Async Loads, New Elements and Unknown Dimensions
The pattern repeats: something loads asynchronously, appears above content that was already visible and pushes it down because nobody reserved its space.
- Images and videos without dimensions: the browser cannot reserve height until it downloads them.
- Web fonts whose metrics differ from the fallback typeface.
- Ads, widgets and embeds that resize themselves when their final content arrives.
How CLS Is Calculated: Impact Fraction x Distance Fraction
CLS does not measure "how much something moved", but how much and how far. The official formula is layout shift score = impact fraction x distance fraction, measured over the viewport and the unstable elements between two frames.
- Impact fraction: the size of the affected area, that is, the union of the element's visible area in the previous and the current frame, as a fraction of the viewport. If it covers 75 % of the screen, it is 0.75.
- Distance fraction: the greatest distance an element moved, divided by the viewport's largest dimension. If it dropped a quarter of the screen height, it is 0.25.
With those numbers, the score for that frame is 0.75 x 0.25 = 0.1875. A detail almost nobody mentions: only elements that already existed and change their starting position count. What jumps is usually the victim; the cause sits above it.
The Session Window: Your Worst Burst, Not Your Total
Since Chrome 91, CLS is not the sum of every shift on the page but the score of its worst burst: the session window, with less than 1 second between shifts and a maximum total duration of 5 seconds. Worth knowing, because Search Console help still describes it as "the total sum of all shift scores". And mind the exception: shifts within 500 ms of a click, a tap or a key press are excluded, but scrolling, dragging and pinching do not count as recent input. If content moves while you swipe, that shift counts.
Thresholds and Today's Data: 0.1 at the 75th Percentile
The official thresholds have not moved: good up to 0.1, needs improvement up to 0.25 and poor above 0.25. They are measured at the 75th percentile of page loads and split between mobile and desktop. A page only passes when all three Core Web Vitals are green at that percentile.
| Metric | Origins with a good value (CrUX, August 2026) |
|---|---|
| CLS | 81.5 % |
| INP | 85.3 % |
| LCP | 68.1 % |
| All three at once | 55.6 % |
Data from the Chrome UX Report: CLS is the metric with the most passing origins after INP.
How to Measure CLS: Field First, Lab Second
Start with field data. The Core Web Vitals report in Search Console groups URLs with a 28-day window: the "Group CLS" column means that 75 % of requests had that score or less, and the group status is set by its worst metric. As a complement, PageSpeed Insights shows field and lab data together, and web-vitals with onCLS() covers your own dashboard.
Then the lab, to reproduce: in Chrome DevTools, the Performance panel includes a Layout shifts track with markers proportional to each shift and an insight listing the culprits. Lighthouse adds audits such as images without width and height. Key warning: the elements in that list are usually victims; the cause is whatever was inserted above them.
img { width: 100%; height: auto; }
.ad-container { min-height: 280px; }
The lab only measures the initial load; field data covers the whole life of the page. If PageSpeed Insights shows a low CLS and the field report shows a high one, you have post-load CLS: lazy-loaded content without reserved space on scroll, content that changes on hover or transitions that take longer than 500 ms.
The Four Causes Behind Almost All CLS
- Images and videos without dimensions: the browser cannot reserve their height until they download.
- Ads, embeds and iframes: the biggest source of CLS on the real web, because their size depends on what gets served.
- Injected content: cookie notices, banners, reordered lists or results added above.
- Web fonts: text is laid out with the fallback typeface and replaced when the real one arrives.
How to Fix It, Cause by Cause
The golden rule is one: if the browser already knows how much space an element will take, there is no shift.
Images, Ads and Dynamic Content: Reserve the Space
For images and videos, declare unitless width and height and pair them with img { width: 100%; height: auto; }: the browser computes the aspect ratio while parsing the HTML and reserves the height before the download. For the rest of the image and lazy-loading work, I will link instead of repeating it: image optimization for SEO and lazy loading for images.
For ads and embeds, reserve space with min-height or aspect-ratio. Do not collapse the slot when no ad is served: removing that space creates as much CLS as inserting it. If you cannot reserve it, place the late content as low as possible in the viewport. For dynamic content, swap it inside a fixed-size container or after a user action, and if it will take longer than 500 ms, reserve the expected space within that first window.
Animations and Fonts: transform and font-display optional
Animating top, left, width, height or box-shadow triggers a re-layout and counts as a shift; animating with transform does not. For type, font-display: optional means the custom font is only used if it is ready for the first paint. Pick a similar fallback too and tune its metrics with size-adjust and the *-override properties: it is all detailed in the font best practices.
What Changed in 2026 (and Why It Isn't 2021 Again)
According to the official Chromium changelog, the Layout Instability API now reports its rectangles in CSS pixels, consistent with getBoundingClientRect(), instead of physical pixels. The CLS score itself does not change: what you need to update are dashboards and RUM setups that assumed physical pixels.
In single-page applications, CLS can be segmented by soft navigation, but field reporting has not yet defined how it will report them. And there is no new metric to chase: CLS is marked stable and its threshold has not moved. The metric retired in March 2024 was FID, replaced by INP.
Common Mistakes When Optimizing CLS
- Chasing a perfect lab score and never checking the 75th percentile of field data.
- Confusing the victim (what moved) with the cause (what was inserted above).
- Forgetting post-load CLS: scroll, lazy loading and transitions.
- Collapsing an ad slot when no ad is served.
CLS and SEO: What Google Actually Says
Google documents in its page experience guide that there is no single page experience signal, that the Core Web Vitals are used by its core ranking systems and that the remaining experience aspects do not directly help you rank. In plain terms: fixing CLS is not a ranking trick, it is avoiding friction and lost clicks.
Conclusion: The Six-Step Checklist
- Check the Core Web Vitals report on mobile and desktop and prioritize URLs marked Poor.
- Compare field and lab: if field is worse, look for post-load CLS.
- Add
widthandheight(oraspect-ratio) to every image, video and embed. - Reserve space for ads and do not collapse the slot.
- Replace layout animations with
transformand applyfont-display: optional. - Measure again with the same field data and wait for the 28-day window.