All posts
Web design & development

How to Fix Cumulative Layout Shift (CLS)

How to fix CLS: find the element that shifts, then fix it by cause, from images and embeds to fonts, banners, animations and late-loading ads and widgets.

By Mehdi Hassan9 min read

The short answer

To fix Cumulative Layout Shift (CLS), first find which element moves, using field data and Chrome DevTools, then fix it by cause: give images and embeds reserved space, match fallback font metrics, overlay banners instead of inserting them, animate with transforms, and reserve slots for ads and widgets. Google's web.dev rates a CLS of 0.1 or less as good.

Key takeaways

  • Find the element that shifts before changing anything; guessing fixes the wrong thing.
  • Most CLS comes from content arriving late without reserved space: images, embeds, ads and banners.
  • Font swaps shift text when the fallback font has different metrics; size-adjust and next/font fix that.
  • Animate with transform and opacity, never top, left, width or height.
  • Lab tools only see shifts during load, so confirm fixes in real-user field data.

Part of Core Web Vitals Explained for Marketers and Founders

Cumulative Layout Shift is the Core Web Vital behind the button that jumps just as someone taps it. It's usually the easiest of the three metrics to fix, but only once you know which element is moving and why. This guide starts with finding the culprit, then goes cause by cause. For how CLS fits alongside LCP and INP, read our guide to Core Web Vitals for marketers and founders.

What is Cumulative Layout Shift and what CLS score is good?

Cumulative Layout Shift measures how much visible content moves unexpectedly while someone uses a page. Google's web.dev defines a good CLS score as 0.1 or less and a poor one as above 0.25, measured at the 75th percentile of page visits, separately for mobile and desktop.

Each layout shift gets a score: the share of the viewport affected, multiplied by how far the elements moved relative to the viewport's largest dimension. Shifts that happen less than one second apart are grouped into a session window lasting at most five seconds, and the page's CLS is the window with the highest total. Shifts within 500 milliseconds of a tap, click or key press are excluded, because the user expects something to change; scrolling doesn't count as input. The full definition is in web.dev's CLS documentation.

CLS score

Rating

What visitors notice

0.1 or less

Good

Little or no visible movement

Above 0.1 to 0.25

Needs improvement

Text or buttons jump now and then

Above 0.25

Poor

Mis-taps and lost reading position

How to fix CLS: where do you start?

Start by confirming that real visitors see the problem and finding the exact element that shifts. Field data tells you which pages fail; browser tools tell you what moved. Fixing CLS without that step usually means reserving space for things that weren't the cause.

  1. Check field data

    Open Search Console's Core Web Vitals report and the field data in PageSpeed Insights. Note which page groups fail CLS and on which device.

  2. Reproduce it in Chrome

    Load the failing page in DevTools with mobile emulation and network throttling. Record in the Performance panel; the layout shifts track marks each shift and highlights the elements that moved.

  3. Make shifts visible

    Turn on Layout Shift Regions in DevTools' Rendering panel to flash shifting areas as the page loads and as you scroll.

  4. Scroll and interact

    Many shifts happen after load: a lazy image, a sticky header, a chat bubble. Scroll the whole page and open menus while recording.

  5. Collect attribution from real users

    If you can't reproduce it, the attribution build of Google's web-vitals library reports the element behind the largest shift from real sessions.

Once you know the element, the cause is usually obvious: something arrived late and pushed content down, or something changed size. The sections below cover each cause in the order we find them most often on marketing sites.

How do you stop images, videos and embeds from shifting the layout?

Give every image, video and embed its space before it loads. Set width and height attributes on images and videos so the browser can compute the aspect ratio, and wrap embeds in a container with a fixed aspect ratio or minimum height.

  • Images. Always include width and height attributes, then let CSS scale them with height set to auto. Modern browsers use the attributes to reserve the right box.
  • Responsive art direction. If mobile and desktop crops differ, set the aspect ratio for each breakpoint in CSS.
  • Video and iframes. YouTube, Vimeo, maps and Calendly-style booking embeds need a wrapper with the CSS aspect-ratio property or a min-height that matches the loaded size.
  • CMS content. Rich text images uploaded by editors often lose their dimensions. Make the CMS store width and height and output them on every image.

In Next.js, the Image component requires width and height, or a fill layout inside a sized parent, which reserves space by default. Builders like Framer and Webflow handle much of this for you, though custom embeds still need care; our guide to Core Web Vitals on Framer, Webflow and Next.js covers each platform. For formats and sizing, see image and video optimization.

How do web fonts cause layout shift, and how do you fix it?

Web fonts cause layout shift when the page first renders text in a fallback font and then swaps to the web font, which has different widths and line heights. Headlines rewrap, paragraphs change length, and everything below them moves.

The fix is to make the fallback take up the same space as the web font. CSS font-face descriptors such as size-adjust, ascent-override and descent-override let you tune a local fallback, like Arial, to match the web font's metrics. In Next.js, the next/font module does this automatically and self-hosts the font files. Preloading the one or two fonts used above the fold also helps them arrive before first render, and font-display set to optional avoids the swap entirely on slow connections at the cost of sometimes showing the fallback.

Fewer font files help on every count. Two families with a small set of weights load faster and swap less than a type system with eight weights and italics.

Overlay late content instead of inserting it above existing content. A cookie banner fixed to the bottom of the viewport doesn't move anything; one injected at the top of the page pushes the whole layout down and counts as a shift.

  • Consent banners. Use fixed or sticky positioning at the bottom or as a modal, never a block inserted before the header.
  • Announcement bars. Render them in the initial HTML, with their height reserved, rather than adding them with JavaScript after load.
  • Personalized or A/B-tested content. Swap content in a container of the same size, and decide variants on the server where you can.
  • Loading states. Skeletons and placeholders should match the final size of the content they replace.

Which animations cause layout shift?

Animations cause layout shift when they change an element's position or size through layout properties such as top, left, width, height or margin, because surrounding content moves too. Animating transform and opacity doesn't trigger layout shifts, so web.dev recommends transform translate and scale instead.

On marketing sites, the usual offenders are sliders that resize as slides load, accordions or tabs that expand on their own, sticky headers that change height while you scroll, and entrance animations that move content into place by changing margins. Expanding an accordion right after a click is fine, because shifts within 500 milliseconds of the input are excluded. Animation-heavy pages can hurt responsiveness too, which our guide to fixing INP covers.

Hypothetical example: a homepage failing CLS on mobile only

For example, suppose a startup homepage passes CLS on desktop but scores 0.3 on mobile in field data. Recording on a throttled mobile profile shows three shifts: the hero headline rewraps when the web font loads, a customer logo strip pops in without reserved height, and a promo bar slides in at the top after two seconds.

The fixes would be a size-adjusted fallback font, width and height on every logo, and moving the promo bar into the initial HTML with its height reserved. Desktop passed because the wider screen meant the headline didn't rewrap and the logo strip took up a smaller share of the viewport.

How do you reduce CLS from ads, chat widgets and other third-party scripts?

Reserve a fixed slot for anything a third-party script injects into the page, and position floating widgets so they overlay content instead of displacing it. Third-party code is the cause we find hardest to control, because it changes without your team deploying anything.

For ad slots, set a min-height based on the most common ad size for that position, and avoid collapsing the slot when no ad fills. Chat widgets should be fixed-position bubbles. Review widgets, social feeds and newsletter embeds need containers sized to their loaded state. If a script injects content you can't size, load it lower on the page, where shifts matter less. Our third-party scripts audit shows how to find which tags are worth keeping at all.

How do you confirm a CLS fix worked?

Confirm it in the lab first, then in field data. Re-record the page in DevTools to check the shift is gone, deploy, and watch the real-user numbers, which update gradually because they cover the previous 28 days.

PageSpeed Insights and Search Console will take a few weeks to reflect the full change. If you need faster feedback, the web-vitals library can send CLS from real sessions to your analytics. Our guide to PageSpeed Insights scores vs real-user data explains why the two often disagree. To stop regressions, build reserved space into your components, so a hero, an embed or a landing page section can't ship without it. That's how we build sites in Next.js, and it also keeps LCP work on the hero from reintroducing shifts.

Frequently asked questions

What causes a high CLS score?
A high CLS score usually comes from content that loads late without reserved space, pushing visible content around. The common causes are images and embeds without dimensions, web fonts that swap with different metrics, banners or promo bars injected at the top of the page, animations that change layout properties, and ads or widgets from third-party scripts that resize after loading.
Does CLS affect SEO?
Yes, modestly. CLS is one of the three Core Web Vitals, which Google uses as part of its page experience signals in ranking. Relevance and content quality matter far more, so fixing CLS won't rescue a weak page. The bigger effect is on visitors: layout jumps cause mis-taps and make a site feel unreliable, which can cost conversions.
Why is my CLS different in PageSpeed Insights and Lighthouse?
PageSpeed Insights shows two kinds of data: field data from real Chrome users over 28 days, and a Lighthouse lab test. The lab test only measures shifts during page load on one simulated device. Real visitors scroll, interact and use different screens, so they hit shifts the lab never sees. When the two disagree, trust the field data.
Does lazy loading cause layout shift?
Lazy loading only causes shift when the lazy-loaded image or embed has no reserved space. With width and height attributes or an aspect-ratio container, the browser holds the box open and the content fills it without moving anything. Never lazy load the hero image, though: that slows Largest Contentful Paint without helping CLS.
Can a single-page app have layout shifts after the first load?
Yes. CLS is measured across the whole time someone spends on the page, not just the initial load, so shifts during client-side navigation or late data fetching count too. Lists that load in after a spinner, or route changes that render content in stages, are common causes. Placeholders that match the final layout prevent them.

· CEO & Design Director

Copywriter, designer and developer, and founder of Designify Studio. Over five years, 60+ projects across 14+ industries, for startups that have raised $300M+.

Want this done for you? Monthly retainer, $6,000.

Your ongoing design and development team, for products that keep shipping. $6,000, per month, flat fee. Book a free 30-minute call to see if it fits.

Schedule Call