Cumulative Layout Shift: the causes and the fix for each
CLS is Good at 0.1 or less at the 75th percentile. The common causes (images, embeds, fonts, banners, animations) and the CSS or HTML that fixes each.
LLaunchScaler·Published ·8 min read
Cumulative Layout Shift is Good at 0.1 or less at the 75th percentile of page loads. To improve it, find the element that moves and reserve its space before it arrives: give images and video width and height or a CSS aspect-ratio, give ads, embeds and iframes a min-height, match your fallback font to the web font with size-adjust, overlay cookie banners instead of pushing content down, and animate with transform rather than top, left, width or height.
web.dev names the most common causes of poor CLS as images without dimensions, ads, embeds and iframes without dimensions, dynamically injected content, and web fonts. Each has a specific CSS or HTML fix, and most take minutes once you know which element is shifting.
What is CLS and what counts as a good score?
CLS measures unexpected movement of visible content. web.dev defines it as "the largest burst of layout shift scores for every unexpected layout shift that occurs during the entire lifecycle of a page," where a burst, or session window, groups shifts less than 1 second apart up to a maximum of 5 seconds. Good is 0.1 or less and poor is above 0.25, at the 75th percentile.
CLS at the 75th percentile
Rating
0.1 or less
Good
Questions, answered
What people ask about this
01
What is a good CLS score?
0.1 or less at the 75th percentile of page loads, measured separately on mobile and desktop. Above 0.1 up to 0.25 needs improvement, and above 0.25 is poor. CLS has no unit; it is a score, not a time.
Each shift's score is the impact fraction times the distance fraction. web.dev's worked example: an element covering half the viewport moves down by 25% of the viewport height, so the union of its old and new positions covers 75% of the viewport (impact 0.75) and it moved 0.25 of the viewport's largest dimension (distance 0.25). The shift scores 0.75 × 0.25 = 0.1875, which on its own is already worse than Good.
Not every movement counts. Shifts "within 500 milliseconds of user input" are flagged and excluded, so a menu that opens on click does not hurt CLS. Scrolls, drags and pinch-zoom do not count as recent input, though, so content that jumps while someone scrolls does count. If CLS is the metric failing your assessment, what a failed Core Web Vitals assessment means covers how the verdict is computed.
How do you find which element is shifting?
Use field data to confirm the problem and lab tools to catch the element. web.dev notes that lab tools "may not show the full CLS of a page as they typically do a basic load," while field data measures shifts "throughout the full life of the page," including after load. A green Lighthouse CLS with a failing field CLS usually means the shift happens after load.
Three ways to catch it:
In Chrome DevTools, open the Performance panel, record a page load, then scroll and interact. The recording shows each layout shift and the elements that moved.
In PageSpeed Insights, filter the diagnostics to CLS. web.dev notes Lighthouse flags images causing CLS through missing width and height, and lists "all the elements that shifted for the page load along with their CLS contribution."
In the field, use the attribution build of the web-vitals library. Its CLS attribution includes largestShiftTarget, a selector for the element that moved most.
Then match the element to one of the causes below.
How do you stop images and video from shifting content?
Always set width and height attributes on <img> and <video>, or reserve the space with CSS aspect-ratio. web.dev says this "ensures that the browser can allocate the correct amount of space in the document while the image is loading." Browsers derive an aspect ratio from the attributes before the image downloads, even when CSS makes the image responsive.
<!-- Reserves a 16:9 box at any rendered width -->
<img src="/img/dashboard.webp" width="1600" height="900" alt="Dashboard with weekly revenue chart"
style="width: 100%; height: auto;">
The browser turns those attributes into aspect-ratio: auto 1600 / 900, so when CSS sets the width to 100%, the height is calculated before a single byte of the image arrives. web.dev notes the auto keyword lets the real image dimensions take over once downloaded, so mismatched attributes still cause a small shift. Use the image's real dimensions.
Two related rules:
In a responsive image with srcset, give every candidate the same aspect ratio, so one pair of width and height attributes is right for all of them.
For art direction with <picture> where crops differ by breakpoint, set width and height on each <source>; web.dev notes Chrome, Firefox and Safari support this.
In Next.js, the next/image component asks for width and height (or fill inside a sized parent), which reserves the space for you. The hero image should also load early for LCP; improving Largest Contentful Paint covers fetchpriority and lazy loading.
How do you reserve space for ads, embeds and iframes?
Give the container a size before its content loads. web.dev suggests a min-height rule to reserve the space, or the aspect-ratio property for responsive content such as ads. Embeds such as videos, maps and social posts "often aren't aware of how large their contents are before they load," so the page must reserve the space.
/* A YouTube-style 16:9 embed */
.video-embed { aspect-ratio: 16 / 9; width: 100%; }
/* An ad slot that serves 250 px or 280 px tall creatives */
.ad-slot { min-height: 250px; }
When the size varies, reserve the smallest likely size and accept a small shift for larger content, as web.dev suggests. Do not collapse the space when nothing loads: web.dev warns that "removing the space set aside for elements can cause just as much CLS as inserting content," so show a placeholder instead. If you cannot reserve space, place late content lower on the page, since content injected near the top of the viewport "usually causes greater layout shifts."
How do you stop web fonts from shifting text?
Make the fallback font take up the same space as the web font, or avoid the swap. When a web font arrives, text laid out in the fallback font re-flows, and web.dev notes that even invisible text "is still laid out using the fallback font," so both FOUT and FOIT can shift content.
The fixes web.dev lists, from quickest to most precise:
Name a close fallback. font-family: "Inter", sans-serif; falls back to a sans-serif; leaving out the generic family makes Chrome fall back to Times, a serif and a worse match.
Preload the critical font with <link rel="preload" as="font" type="font/woff2" crossorigin> so it is more likely to be ready at first paint.
Use font-display: optional for fonts that are nice to have. The web font is only used if it has loaded by initial layout, so there is no swap and no shift.
Adjust the fallback's metrics with size-adjust, ascent-override, descent-override and line-gap-override, so the swap changes the look but not the size.
A metric-matched fallback looks like this, using the values from web.dev's own example:
Next.js does this for you: next/font loads fonts "with no layout shift," and its adjustFontFallback option, on by default for Google fonts, "sets whether an automatic fallback font should be used to reduce Cumulative Layout Shift."
How do you stop banners and injected content from pushing the page?
Do not insert content above existing content without a user action. web.dev calls out banners and forms that "pop-in at the top or bottom of the viewport" and shift the page. Either reserve their space from the first paint, with a placeholder or skeleton, or take them out of the document flow by overlaying them on the content.
Element
Shift-free approach
Cookie or consent banner
position: fixed at the bottom of the viewport, overlaying content
Promo or announcement bar
Render it in the server HTML so it is there at first paint, or overlay it
Newsletter or signup prompt
Overlay as a modal or slide-in, never inserted into the article flow
"Load more" lists and feeds
Load on a button press (shifts within 500 ms of input are excluded), prefetching the data first
Content loaded while scrolling
Reserve the height of the incoming items before they render
A consent banner inserted at the top after load moves every element below it, and so does a promo bar added by a client-side script, which is why both belong in the server HTML or on top of the content.
Which CSS properties cause shifts when animated?
Animating top, left, width, height or margin moves or resizes elements in the layout, and web.dev notes that changing top and left causes layout shifts "even when the element being moved is on its own layer." Animate with transform instead: translate() to move, scale() to resize.
web.dev says composited transform animations "can't impact other elements, and so don't count toward CLS." Respect prefers-reduced-motion for visitors who ask for less movement.
What else keeps CLS low?
Make pages eligible for the back/forward cache. web.dev calls bfcache eligibility "a highly effective technique for keeping CLS scores low," because a page restored from the cache appears exactly as it was left, with none of the load-time shifts. Some pages are ineligible, and web.dev's bfcache guide explains how to test for and remove the reasons.
Also check what your lab tool reports beyond Core Web Vitals. The same PageSpeed Insights run that shows layout shifts has other audit categories; the Agentic Browsing audits in PageSpeed Insights cover one of them.
Find the elements that shift on your page
To see your field CLS and the usual causes in one pass, run the free scan on LaunchScaler with the page's URL, no account needed. Its speed checks read CLS from real Chrome users at the 75th percentile, and its rendered-page checks flag images, iframes and embeds that render without reserved dimensions and web fonts that block or swap text. Fix the element that moves most, then confirm with a DevTools recording before the field data catches up over 28 days.
web.dev lists the most common causes as images without dimensions, ads, embeds and iframes without dimensions, dynamically injected content, and web fonts. Banners inserted above content and animations of top, left, width or height cause shifts too.
03
Do width and height attributes on images still matter?
Yes. Browsers compute a default aspect ratio from the width and height attributes before the image loads, so a responsive image with width 100% still reserves the right height. Without them the image takes no space until it arrives and pushes content down.
04
Why is my CLS different in Lighthouse and PageSpeed Insights field data?
Lighthouse measures a basic page load, while field data measures shifts across the whole life of the page, including ones after load such as lazy-loaded content appearing during scrolling. A green lab CLS does not rule out a field problem.
05
Do shifts after a click count toward CLS?
Not if they happen within 500 milliseconds of a tap, click or keypress; those shifts are flagged as having recent input and excluded. Scrolling and dragging do not count as recent input.
The Strict-Transport-Security value for testing, production and preload, what the preload list requires, and why removal from it takes weeks to months.