CLS measures how much your page layout jumps around while it loads. When elements shift unexpectedly — pushing content down or moving buttons as users try to click them — it creates a poor experience that Google penalises with lower rankings. Here is how to diagnose and fix it.
📊 Diagnose CLS Issues All Performance Tools →CLS is one of Google's three Core Web Vitals. It measures the total amount of unexpected layout movement that happens during a page's lifetime. Every time an element moves after the initial render — pushing other content out of the way — CLS increases. A high CLS score means your page is visually unstable and creates a frustrating user experience.
When a browser encounters an img tag without width and height attributes, it does not know how much space to reserve. When the image loads, the browser inserts it and pushes everything below it down — causing a significant layout shift. Fix: add width and height to every img tag.
When a page loads with a system font and then swaps to a web font (Flash of Unstyled Text), text can reflow and shift surrounding elements. Fix: preload critical fonts and use font-display: optional or font-display: swap with font metric overrides.
Ad slots that load content dynamically after the page renders are a major source of CLS. Reserve space for ad units with explicit dimensions even before the ad loads. Use min-height on ad containers.
Cookie banners, chat widgets and newsletter popups injected above existing content push everything down. Use fixed or absolute positioning so they overlay content rather than pushing it.
Most people treat CLS as a black box that spits out a decimal. It is not. It is a simple formula, and once you can see the arithmetic you can predict which shifts will hurt you and which will barely register — which is the difference between fixing the right thing and fixing the visible thing.
Every individual layout shift is scored as:
The impact fraction is how much of the viewport was affected — the union of where the unstable element was before and where it ended up, as a proportion of the visible screen. The distance fraction is how far it travelled, as a proportion of the viewport's largest dimension.
Two things follow immediately. A large element moving a short distance scores badly. A small element moving a long distance scores badly. But a large element moving a long distance scores catastrophically, because the two fractions multiply — this is why a banner injected at the top of the page, which pushes the entire viewport's worth of content down by several hundred pixels, is the single worst thing you can do to a CLS score.
This changed in June 2021 and a great deal of stale advice has not caught up. CLS is not the total of every shift across the page's life. It is the score of the single worst session window — a burst of shifts where each is no more than 1 second apart, and the whole window is capped at 5 seconds.
The practical consequence is significant for long pages and single-page applications. A page that shifts a little every few seconds for ten minutes is no longer penalised as though those shifts accumulated forever. But it also means that one bad burst determines your entire score. Fixing four small, well-behaved shifts while leaving one 3-second cascade of injected content untouched will not move your number at all.
Shifts that occur within 500 milliseconds of a user interaction — a click, a tap, a keypress — are excluded from CLS. This is deliberate: if the user expands an accordion and the content below moves, that movement was expected and requested.
This exclusion is also the trap most commonly fallen into during debugging. You open the page, click around, see nothing shift, and conclude the page is fine — while your real score is being generated in the first two seconds of load, before any user has touched anything, which is exactly the window you skipped past.
Google's page experience signals are computed from the Chrome User Experience Report (CrUX) — anonymised measurements from real Chrome users on real devices and real networks. Not from a synthetic test. This has three consequences that determine how you should debug.
A Lighthouse run gives you a lab CLS on a simulated device, in a controlled 5-second window, with no cookie banner acceptance, no ad auction, no third-party script that only fires for logged-in users. It is invaluable for finding shifts. It is not the number Google uses.
The standard advice — put width and height on your images — is correct, but the reason matters, because the reason is what tells you when it will fail.
Modern browsers use the width and height attributes to compute a default aspect-ratio for the element before the image data arrives. The box is then reserved at the correct proportions immediately. Your CSS can still resize the image freely — width: 100%; height: auto; works exactly as before. The attributes are not sizing the image; they are declaring its shape.
Which means the fix breaks in a specific, common circumstance: if your CSS sets height: auto but your HTML attributes give the wrong ratio, you have reserved a box of the wrong shape, and the image will still shift when it lands and corrects itself. The attributes must be the image's true intrinsic dimensions, not round numbers someone typed in.
aspect-ratio property inside your media queries instead.<iframe> embeds — video players, maps, social posts. They are frequently the largest shifting element on a page and almost never carry dimensions. Wrap them in a container with a fixed aspect-ratio.content-visibility: auto without a matching contain-intrinsic-size. The browser skips rendering the content and reserves zero height, then reserves the real height when it scrolls into view. That is a layout shift you introduced while trying to optimise.Web font shifts happen because the fallback font and the web font have different metrics — different character widths, different ascent and descent. Text laid out in the fallback occupies a different number of lines than the same text in the web font, so when the swap happens, everything below reflows.
font-display controls the timing of that swap, and the choices are genuinely different trade-offs rather than better and worse options:
| Value | Behaviour | CLS effect |
|---|---|---|
| swap | Fallback shows immediately, swaps whenever the font arrives | Guaranteed shift — but text is always readable |
| block | Text invisible for ~3s, then fallback | No shift if font arrives in time — but invisible text (FOIT) |
| optional | ~100ms block; if the font is not ready, the fallback is used for the whole page view | No shift, ever — but some users never see your font |
font-display: optional is the only value that structurally cannot cause a layout shift, because it never swaps mid-render. The cost is that a user on a slow first visit sees your fallback font. For most content sites this is an entirely acceptable trade; the font is not the product.
You can eliminate the shift without giving up the swap, by making the fallback font occupy the same space as the web font. The @font-face descriptors size-adjust, ascent-override, descent-override and line-gap-override let you define an adjusted local fallback whose metrics match your web font. Text laid out in the adjusted fallback takes up the same lines and the same height — so when the real font arrives, the swap is invisible and nothing moves.
Finally: preloading a font requires crossorigin, even for same-origin files. <link rel="preload" as="font" type="font/woff2" crossorigin>. Omit it and the browser fetches the font twice — once for the preload, once for the actual use — which is slower than not preloading at all. It is one of the most common performance own-goals on the web.
This is where most real-world CLS actually lives, and it is the hardest category because the shifting content is not yours.
Setting a min-height on an ad container is right in principle and frequently wrong in practice, because ad networks serve variable sizes into the same slot. Reserve the height of the size that is served most often, not the smallest. If you reserve 250px and a 600px unit lands, you have shifted by 350px and gained nothing.
And do not collapse an empty slot. If the ad fails to fill, the reserved space collapsing to zero pushes everything up — an upward shift scores exactly the same as a downward one.
A slot at the very top of the page, above the article, has the maximum possible impact fraction — the whole viewport moves. The same ad placed in the middle of the page, where less content sits below it, scores a fraction of the same penalty. Placement is a CLS decision, not just a revenue one.
These should never be in the document flow. Use position: fixed so they overlay the page rather than displacing it. A banner that pushes the entire page down is, in CLS terms, the single most damaging element you can add — and unlike an ad, it is entirely within your control.
A loading placeholder only helps if the content that replaces it is the same size. A 100px skeleton replaced by a 340px card has not prevented a shift, it has staged one — and given the user a moment of confidence before the page moves under them.
An animation that changes an element's geometry forces the browser to recalculate layout — and every recalculation that moves surrounding content is a layout shift. Animating top, left, width, height, margin or padding triggers this on every frame.
The same visual effect achieved with transform: translate() or transform: scale() does not. Transforms are applied at the compositing stage, after layout — the element moves on screen without the browser re-laying-out anything around it. The result to the eye is identical; the result to CLS is zero versus a running penalty on every frame.
transform. If you are animating visibility, animate opacity. Between them these two properties cover the overwhelming majority of interface animation, and neither one can cause a layout shift.One exception worth knowing: an expanding accordion or "read more" that animates height will shift the content below it, but if it is triggered by a click it falls inside the 500ms user-input exclusion window and is not counted. The animation must actually be a response to the interaction — an accordion that opens itself on a timer is counted in full.
Guessing is the slow way. Every major browser will simply tell you which node moved.
LayoutShift PerformanceObserver API. For production monitoring, observe entries of type layout-shift. Each entry carries a sources array naming the DOM node that moved, its previous rectangle and its current one. This lets you log real shifts from real users, which is the only place the shifts that actually count are visible.Google considers a CLS score of 0.1 or less to be Good. Scores between 0.1 and 0.25 need improvement. Scores above 0.25 are Poor. CLS is measured as a unitless decimal — it represents the fraction of the viewport that shifts multiplied by the distance of the shift.
The most common causes are images without width and height attributes (the browser does not know how much space to reserve), web fonts that cause text to reflow when they load, ads or embeds that inject content above existing content, and dynamically injected content like cookie banners or newsletter popups.
Add explicit width and height attributes to every img element on your page. This tells the browser exactly how much space to reserve before the image loads, preventing layout shift. For example: img src='photo.jpg' width='800' height='600' alt='description'. CSS can still resize the image — the attributes just establish the aspect ratio.
No — not since June 2021. CLS is the score of the single worst session window: a burst of shifts each occurring within 1 second of the last, with the window capped at 5 seconds. One bad burst therefore determines the entire score, and fixing several small shifts while leaving the worst burst in place will not improve the number.
Lighthouse reports lab data from a single simulated load; Search Console reports field data from the Chrome User Experience Report, gathered from real users. The threshold is applied at the 75th percentile of real visits, and the data is a 28-day rolling window. Cookie banners are a frequent cause of the discrepancy — a returning user who has already consented sees a different layout from the one the lab test measures.
Animating layout properties such as top, left, width or height causes layout shifts on every frame. Animating transform (translate or scale) and opacity does not, because these are applied at the compositing stage after layout is computed. The visual effect is the same; the CLS cost is zero.
No. Any layout shift occurring within 500 milliseconds of a user interaction — a click, tap or keypress — is excluded, because the movement was expected. This is also why manually clicking around a page is a poor way to debug CLS: the score is generated during load, before any interaction takes place.
Run the CLS & CSS Linter and get actionable results in minutes. Pay as you go — no subscription needed.
Diagnose CLS Issues →aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical website tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.