Cumulative Layout Shift (CLS): What It Is and How to Fix It

What is cumulative layout shift (CLS)?
Cumulative layout shift (CLS) is a Core Web Vitals metric that measures how much visible content moves unexpectedly while a page loads and while users stay on it. So a lower score means a more stable page. Google treats a CLS of 0.1 or less as good.
Here is the everyday version. You are reading an article, a late image loads, and the text jumps down. Or you reach for a button, an ad appears, and you tap the wrong thing. Both moments are layout shifts.
Our team sees this often in audits. Visitors rarely name the problem, but they feel it. Then their trust drops, and they leave. In this guide we cover measurement, thresholds, causes and fixes, based on the official web.dev documentation.
We do not go deep on LCP or INP here. If you want all three metrics together, read our Core Web Vitals guide. So this article stays on visual stability.
Why does cumulative layout shift matter?
CLS matters because it pushes users into mistakes. Misclicks, a lost reading position and a jumping checkout button are the classic examples. Also, these problems reduce conversions.
Search is the second angle. Google also includes Core Web Vitals among its page experience signals. However, fixing CLS alone will not lift rankings dramatically, because content relevance always comes first. Still, it can separate two otherwise similar pages.
For the wider speed picture, see our article on how site speed affects SEO. We recommend prioritizing CLS for three reasons:
- It reduces frustration caused by misclicks.
- It helps lower abandonment in forms and checkout steps.
- It exposes technical debt early, so the cost does not grow later.
In short, CLS is a small-looking metric with a direct effect on trust. It is also usually cheaper to fix than the other two vitals.
A single image with two missing attributes can cost you more than a slow server ever will. Shifts also hurt brand perception: a corporate site that jumps around looks careless, no matter how good its content is.
How do you calculate a cumulative layout shift score?
You score a single shift with one formula: impact fraction times distance fraction. web.dev writes it as layout shift score = impact fraction * distance fraction.
The impact fraction shows how much of the viewport the unstable elements affect between two frames. The distance fraction divides the greatest distance any unstable element moved by the viewport's largest dimension.
Now a worked example (hypothetical numbers, not from a real site). A late image pushes content that covers half the viewport, so the impact fraction is 0.5. The content drops by 20 percent of the screen height, so the distance fraction is 0.2. The score is 0.5 x 0.2 = 0.10.
As a result, one image can push your page to the threshold on its own. So small shifts deserve attention. Shifts also add up: 0.04 + 0.05 + 0.03 equals 0.12, so three harmless-looking jumps can land you above 0.1.
Therefore you should think about the total experience, not a single event.
What is a good CLS score?
web.dev says sites should aim for a CLS of 0.1 or less for at least 75 percent of page loads. So you judge the 75th percentile, not the average.
| Status | CLS value | Meaning |
|---|---|---|
| Good | 0.1 or less | Stable page; the target zone |
| Needs improvement | Between 0.1 and 0.25 | Shifts are noticeable; plan fixes |
| Poor | Above 0.25 | The experience is seriously broken |
Mobile and desktop get measured separately. Screens are smaller on mobile, so the same shift creates a larger impact fraction. That is why mobile scores are often worse.
Treat these thresholds as targets, not as verdicts. What matters is a trend that moves in the right direction and real-user data that reaches green at the 75th percentile.
For example, if desktop shows 0.05 and mobile shows 0.22, mobile is clearly the priority. When you report, share both values, the affected URL groups and the trend together. Decision makers then see the priorities faster.
What is a layout shift session window?
A session window groups shifts that happen close together. According to web.dev, the gap between shifts stays under 1 second, and the whole window lasts at most 5 seconds.
Your CLS is the total score of the largest window during the page's life. So you do not add up every shift from the entire visit. Instead, the worst burst decides the result.
This matters because some pages stay open a long time. In single page apps and infinite scroll, the older approach could inflate the score the longer the page stayed open. Windows reduce that unfairness.
The practical lesson is simple. Clean up shifts at page load, but also check elements that appear while users scroll. Also, the worst window can start in the middle of the page.
Which layout shifts do not count toward CLS?
Shifts within 500 milliseconds of a user interaction count as expected. For example, clicks, taps and key presses belong here. The browser sets the hadRecentInput flag on those shifts, so you can exclude them.
Continuous interactions such as scrolling, dragging and pinch zoom do not qualify. So if content jumps while a user scrolls, it still hits your score.
This rule also gives you a design principle. For instance, a menu that opens after a button press is fine. An element that appears without any user action is the problem.
- An accordion that opens on click: does not count.
- A banner that appears on its own at load: counts.
- Cards that load during infinite scroll: count if you reserved no space.
Therefore tying content to a user action is a cheap and reliable way to lower CLS.
How do you measure CLS?
You measure CLS in two ways: lab data and field data. First, a lab tool gives a score in a controlled load. Second, field data reflects what real visitors experienced.
To start, use these tools:
- PageSpeed Insights: shows real-user data (CrUX) next to the lab result.
- Chrome DevTools Performance panel: the Layout Shifts track marks shifts with purple bars.
- Lighthouse: lists images without dimensions and the elements that shifted.
- Search Console Core Web Vitals report: shows which URL groups have problems.
For Lighthouse basics, see our guide to Lighthouse performance testing. Chrome's live metrics view also helps you watch CLS while you interact with the page.
Look at field data first, then find the cause in the lab. Otherwise you chase problems that do not exist.
Why do field and lab CLS data differ?
A lab test runs on one device, one connection and usually the first load. Real users scroll, close cookie banners, switch tabs and use many different devices.
So seeing 0.02 in the lab and 0.18 in the field is not surprising. The gap usually comes from these sources:
- Late ads and embeds that load while users scroll.
- Consent or personalization scripts that only run for real visitors.
- Fonts that arrive late on slow connections.
- Image ratios that change across screen sizes.
Therefore a green lab result is not enough. To reproduce a shift, throttle the network, clear the cache and test by scrolling down the page.
Field data also takes time. CrUX works on a rolling period, not day by day, so do not expect instant confirmation after a fix.
What are the most common causes of CLS?
The web.dev optimization guide names four main causes: images without dimensions, ads and embeds without dimensions, dynamically injected content and web fonts. We also add animation mistakes to that list.
| Cause | Typical symptom | Core fix |
|---|---|---|
| Image without dimensions | Text jumps down when the image arrives | width and height attributes, aspect-ratio |
| Ads and iframes | Content moves when the ad fills | Reserve space with min-height |
| Dynamic content | A banner or form appears on top | Fixed-size container, user trigger |
| Web font | Text wraps again | font-display, size-adjust, preload |
| Animation | An element pushes its neighbors | transform instead of top and left |
Most of these share one root: the browser does not know how much space the content needs. So every fix does the same thing, which is to declare the size in advance.
Next we go through each cause. First, the one we see most often: images.
How do images without dimensions cause layout shift?
If the browser does not know an image's size, it reserves no space. Then, when the image arrives, the content moves down. So this is the most common and easiest cause to fix.
The fix is simple. Add width and height attributes to every img and video element. Modern browsers compute the aspect ratio from those values and reserve the space before the file loads. The ratio survives even if your CSS sets the width to 100 percent.
For responsive images, make sure every srcset variant has the same aspect ratio. Otherwise the browser can still shift when the chosen variant arrives.
Lighthouse catches images without dimensions. File size is a separate topic; for that, read our guide on image optimization.
If you use lazy loading, do not skip the size information. Lazy loading reduces delay, but it raises the shift risk when you reserve no space.
How do you reserve space for ads, embeds and iframes?
Ad slots, video embeds and social widgets often arrive without dimensions. When they fill late, everything below them moves down.
web.dev recommends this approach: reserve space in advance with min-height or aspect-ratio, and place late-loading content lower in the viewport. Use a placeholder instead of injecting content without any user interaction.
However, ad networks can return several sizes. In that case, reserve space for the size that returns most often. If no ad comes, you keep an empty slot, but an empty slot does less harm than content that jumps.
- Give embedded video an aspect-ratio such as 16 / 9.
- Set a fixed height on maps and social embeds.
- Avoid placing late-filling ads at the top of the page.
You must balance revenue and experience. When you do, compare short-term revenue against long-term trust.
How do web fonts cause layout shift?
The browser first draws text with a fallback font, then redraws it when the web font arrives. If the two fonts have different letter widths, lines wrap again and the text block moves.
web.dev lists several measures. Preload critical fonts with link rel=preload. Use font-display: optional to avoid re-layout entirely. Pick a suitable fallback font, and use size-adjust and ascent-override to bring the fallback close to the web font.
| Method | Effect | Watch out for |
|---|---|---|
| preload | The font arrives earlier | Use it only for critical fonts |
| font-display: optional | No re-layout | The fallback may stay on slow connections |
| size-adjust and ascent-override | Fallback metrics move closer | Test the values |
Font choice also affects performance. For the design side, read how to choose web typography. Also stop loading weights you never use, such as unused italics. That way you gain speed and lower the shift risk.
Do dynamic content and cookie banners cause layout shift?
Yes, they can. If a cookie banner gets inserted at the top of the page, it pushes all content down. Also, notification bars, signup boxes and campaign strips do the same.
The fix is to take these elements out of the document flow. Pin the banner to the bottom, or reserve a slot at the top from the start. For content that needs a user trigger, such as a Load more button, the new content arrives after user input, so it does not count.
For pop-ups, you can also look at exit-intent popups. A well-designed pop-up overlays the page and does not push it.
- Make the banner fixed-position so it does not push content.
- For example, refresh new content inside a fixed-size container.
- Tie late server content to a user action.
How do animations affect CLS?
When you animate properties such as top, left, box-shadow and box-sizing, the browser recalculates layout and neighbors can move. Those shifts hit your score.
web.dev's answer is clear: use transform for animations. transform: translate() and transform: scale() do not change the layout flow, so they create no shift. They also run smoothly on the GPU.
For example, in a menu that slides down, use translateY instead of changing top. The visual result is the same, but the surrounding elements stay put.
If you want inspiration, see our micro animation examples.
How do you fix CLS on WordPress and ecommerce sites?
On sites that use ready-made themes and plugins, the shift usually comes from one component. Sliders, pop-ups, review plugins, live chat bubbles and product galleries are the usual suspects.
In ecommerce, three changes make a big difference: give product card images dimensions, stop price and stock data from filling late, and move the cart notice into a fixed area. However, a shift at checkout costs you orders directly.
On the technical side, check these points:
- Does the theme's slider define a height?
- Does the review plugin inject content late?
- Does the live chat bubble push the page, or does it overlay it?
- Do the product gallery images have width and height?
For platform choice, read WordPress vs custom website. When you own the code, fixing a shift at its source lasts longer than stacking plugin on plugin.
How do you measure CLS in single page apps?
In single page apps (SPA), however, route changes do not count as browser loads. So a user can visit many screens, yet the browser sees one page lifetime.
Session windows soften this, because only the worst window counts. Still, you should use skeleton screens on every route change and keep container sizes fixed while you wait for data.
For example, a list that stays empty until data arrives can grow by hundreds of pixels. So draw a placeholder at the expected height. Then the elements below stay in place when the content fills.
Another trap on route changes is to delete the old content and add the new content later. Keep the old screen until the new one is ready, or use a fixed-size container.
JavaScript weight matters here too. Late scripts inject content and create shifts; for details, see how JavaScript affects site speed.
How do you improve cumulative layout shift step by step?
You improve cumulative layout shift in a systematic loop: measure first, find the biggest shift, fix it and measure again. The sequence below is the framework our team follows on projects.
- Find the problem URL groups in Search Console.
- Compare field and lab data for the same URL in PageSpeed Insights.
- Open the Layout Shifts track in DevTools and find the largest shift.
- Classify the element: image, ad, font, dynamic content or animation.
- Apply the fix: size attributes, reserved space, font settings or transform.
- Check on a slow network and a mobile device that the shift is really gone.
- Wait for field data to update and report the result.
The key principle is this: fix the single shift that creates the most score first. On most sites a few elements produce most of the total.
As a code-level example, writing width="800" height="450" on the img tag and using height: auto in CSS keeps the ratio and reserves the space in advance.
In what order should you fix CLS problems?
However, not every fix costs the same or pays the same. To set priority, weigh impact, effort and risk together. The table below gives a general starting order; your site may differ.
| Order | Fix | Effort | Expected impact |
|---|---|---|---|
| 1 | Add width and height to images | Low | Usually high |
| 2 | Give ad and embed slots a min-height | Low to medium | High on affected pages |
| 3 | Move the cookie banner out of the flow | Low | Medium |
| 4 | Tune font loading | Medium | Medium |
| 5 | Move animations to transform | Medium | Low to medium |
This ordering is a starting point from field experience, not a guarantee. The real order comes from the largest shift you see in DevTools.
Also measure again after every step. That way you see which change worked, and you can roll back the one that did not.
What does a CLS diagnosis look like on a real page?
Let us walk through a hypothetical example (an illustrative scenario, not a real client). A blog template shows a mobile CLS of 0.21, and Search Console places the page in the needs improvement group.
In the DevTools Layout Shifts track, you see three shifts. First, the cover image arrives late. Second, a cookie banner appears at the top. Third, the body font swaps in.
| Shift | Possible score share | Fix |
|---|---|---|
| Cover image | 0.12 | Add width, height and aspect-ratio |
| Cookie banner | 0.06 | Pin the banner to the bottom |
| Font swap | 0.03 | Use preload and size-adjust |
These values are an example calculation. They add up to 0.21, and fixing the image alone brings the page to 0.09, below the threshold.
Notice that the biggest gain comes from the simplest fix. So in a diagnosis you look at the largest shift first, then close the rest in order. You see each step's effect and tell a clear story in your report.
Does the back/forward cache affect CLS?
Yes, in a positive way. According to web.dev, when a page is restored from the back/forward cache, its CLS value should reset to zero, because users experience it as a distinct page visit.
So improving bfcache eligibility removes reload-related shifts for users who come back with the back button. You can test whether your page qualifies in the Application panel of Chrome DevTools.
However, some old habits, such as an unload listener, can break eligibility. Add a bfcache check to your technical audits.
For a general technical health check, see our list of technical SEO tips.
How do CLS, LCP and INP work together?
Core Web Vitals has three metrics: LCP measures loading speed, INP measures interaction response and CLS measures visual stability. So each one captures a different user experience.
We do not cover LCP in detail here, but it helps to know where the metrics collide. For example, loading a hero image early improves LCP, yet if you give it no dimensions, you can damage CLS.
Similarly, heavy JavaScript hurts INP, and it can also hurt CLS through late injected content. On mobile the problems grow; see mobile-first design for the approach.
Therefore do not optimize one metric and break another. Measure all three after every change.
How do you keep monitoring CLS over time?
However, a CLS fix is not a one-time job. A new plugin, ad network or theme update can bring shifts back. So make monitoring a routine.
- Check the Search Console Core Web Vitals report every month.
- Save the PageSpeed Insights result for key templates: home, category, product, blog.
- Run a quick DevTools shift check after every major release.
- Write a space-reservation plan before you add a new ad or third-party script.
Make ownership clear inside the team. The designer owns dimensions and reserved space, the developer owns fonts and load order, and the marketing team owns ad and banner additions.
In the end, good CLS is not a one-off fix. It is a habit built into your publishing process.
What are the most common CLS mistakes?
We see these mistakes often in audits:
- Looking only at the lab score and missing the real field problem.
- Adding width and height to an image and then breaking the ratio in CSS.
- Reserving ad space but choosing the wrong size.
- Removing a font completely instead of fixing how it loads.
- Skipping mobile tests, although the mobile score is often worse.
Another mistake is trusting one measurement, because one test hides a lot. A shift sometimes shows up only on a certain device or connection speed. So test with several scenarios.
Finally, do not focus only on the score. Open the page like a real user, scroll and press buttons. So if a jump bothers you, your visitors feel it too.
When should you ask an expert for help?
If the problem is one image, you can solve it yourself. However, if the shift comes from a mix of theme, plugins and third-party scripts, finding the cause takes time.
As Talha Aslan and team, we review CLS together with field data in technical SEO audits. When needed, we rebuild the template in our web design work and define a monitoring routine within our SEO consulting.
First, we fix the goal: which templates, which devices and which threshold. That gives us a measurable before-and-after comparison at the end. When the work is done, we hand over the monitoring list.
To prepare, gather your Search Console report, your plugin list and your ad networks. Then we can go straight to the cause in the first call.
Sources: web.dev CLS, web.dev optimize CLS and Google Search Central Core Web Vitals.




