Skip to content
Now accepting Q2 projects — limited slots available. Get started →
SEO · Updated Apr 30, 2026

What is Cumulative Layout Shift (CLS)?

Cumulative Layout Shift (CLS) is a Core Web Vital that measures the total unexpected visual movement of page elements during loading.

What is Cumulative Layout Shift (CLS)?

Cumulative Layout Shift (CLS) is a Core Web Vital that measures how much stuff jumps around on your page while it's loading. Google shipped it in May 2020, started using it for rankings in June 2021. You want ≤0.1 (good). Anything >0.25 is poor. It's unitless — just a score.

The metric catches every unexpected layout shift that happens without user input. Images popping in without dimensions. Web fonts swapping. Ads shoving content down. CLS multiplies the impact fraction (how much of the viewport shifted) by the distance fraction (how far things moved).

In 2024, Google changed the calculation to use "session windows" — it groups shifts into max 5-second bursts with 1-second gaps between them, then reports the worst window's total. We've fixed CLS on probably 50+ production sites. It's the Core Web Vital that third-party scripts break most often.

How it works

CLS tracks layout shifts that happen without a preceding user interaction (click, tap, keypress). Each shift gets a score:

layout shift score = impact fraction × distance fraction

Impact fraction is the percentage of viewport area the unstable element occupies in both positions. Say a 200px element moves down 100px in an 800px viewport. The impacted area is 300px total, so impact fraction is 300 / 800 = 0.375.

Distance fraction is how far any element moved, divided by the viewport's largest dimension. Same example: 100 / 800 = 0.125. Shift score: 0.375 × 0.125 = 0.047.

Shifts group into session windows — bursts where each shift happens within 1 second of the previous one, max 5 seconds total. CLS reports the largest window's score.

You can measure it with:

  • Lighthouse (lab data, simulated)
  • Chrome UX Report (CrUX) (field data, real users)
  • web-vitals JS library (v4.x) for RUM
import { onCLS } from 'web-vitals';

onCLS((metric) => {
  console.log('CLS:', metric.value);
  // Send to analytics
});

CrUX field data is what Google uses for ranking. Lab scores lie constantly because Lighthouse doesn't scroll or trigger lazy content like real users do.

When to use it

CLS matters everywhere, but some scenarios need extra attention.

Watch closely when:

  • You've got above-the-fold images or video — always set explicit width and height or use CSS aspect-ratio
  • Loading web fonts — font swap causes reflow. Use font-display: optional or preload critical fonts
  • Third-party ads or widgets inject dynamically — reserve space with min-height containers
  • Using skeleton loaders — make sure placeholders match final dimensions exactly
  • SPAs with client-side rendering — route transitions trigger shifts if content pops in

Less critical when:

  • Pages are entirely static
  • Only shifts happen after explicit user interaction (excluded from CLS by design)
  • Internal tools where SEO doesn't matter (though UX still does)

Biggest wins we've seen? Setting image dimensions and reserving ad slot space. Every time.

CLS vs alternatives

CLS is one of three Core Web Vitals:

Metric Measures Good threshold Category
CLS Visual stability ≤ 0.1 Layout stability
LCP Loading performance ≤ 2.5s Perceived load speed
INP Interactivity ≤ 200ms Responsiveness

CLS is the only non-time-based metric — it's unitless. It's also the only one that accumulates throughout the page lifecycle, not just during initial load. This makes it uniquely annoying.

A page can pass CLS in Lighthouse but fail in CrUX because real users scroll and trigger lazy content that shifts layout. We've seen this break production launches more than once.

Before INP replaced FID in March 2024, CLS was the metric teams ignored most. Now that everyone's focused on INP, we're seeing CLS regressions creep back — especially on editorial sites with heavy content.

Real-world example

We worked on a Next.js 14 e-commerce site where product listing pages had CLS of 0.31. Poor range. Two culprits: product images using raw <img> tags instead of Next.js Image component (which handles dimensions automatically), and a promo banner loading 800ms after paint, shoving the product grid down 120px.

Fixes took two hours. Switched to next/image with width/height props. Added min-height: 80px to the banner container. Preloaded the banner API call.

CLS dropped to 0.04 in CrUX within the next 28-day collection window. Organic traffic to those listing pages went up 11% the following month. Partly the CWV fix, partly seasonal — we couldn't isolate it perfectly. But the correlation was there.

Frequently asked questions about Cumulative Layout Shift (CLS)

Is CLS the same as LCP?
No. CLS (Cumulative Layout Shift) measures visual stability — how much page content moves unexpectedly. LCP (Largest Contentful Paint) measures loading performance — how quickly the largest visible element renders. They're both Core Web Vitals but track completely different aspects of user experience. A page can have great LCP (fast load) but terrible CLS (content jumping around), or vice versa. You need to optimize both independently. CLS is a unitless score (good ≤ 0.1) while LCP is measured in seconds (good ≤ 2.5s).
When did CLS become a Google ranking factor?
Google announced Core Web Vitals — including CLS — as ranking signals in May 2020 and began rolling them into search ranking in June 2021 as part of the Page Experience update. The rollout completed by August 2021 for mobile and February 2022 for desktop. In 2024, the Page Experience signals were folded into Google's broader ranking systems rather than being a standalone signal, but CLS still matters for rankings. The CLS calculation itself was updated in 2024 to use session windows instead of summing all shifts, which made scores more fair for long-lived pages.
What's the best way to fix CLS issues?
The most impactful fixes in order: (1) Set explicit width and height on all images and video embeds — this lets the browser reserve space before the asset loads. (2) Preload web fonts or use `font-display: optional` to prevent text reflow. (3) Reserve space for dynamic content like ads, banners, and cookie consent bars using CSS `min-height` on their containers. (4) Avoid injecting content above existing content after initial render. (5) Use CSS `aspect-ratio` for responsive embeds. The `web-vitals` library (v4.x) and Chrome DevTools Performance panel both show which elements caused shifts, so start by diagnosing before guessing.
Why is my CLS different in Lighthouse vs CrUX field data?
Lighthouse runs a simulated page load in a controlled environment — it doesn't scroll, click, or interact with the page beyond initial load. CLS in the field (CrUX data) captures shifts across the entire user session, including shifts triggered by scrolling, lazy-loaded images, infinite scroll, sticky headers repositioning, and late-loading ads. Field CLS also reflects real network conditions, device performance, and browser extensions. It's common to see a 0.02 CLS in Lighthouse and a 0.18 in CrUX. Always prioritize field data — that's what Google uses for ranking decisions.
Get in touch

Let's build
something together.

Whether it's a migration, a new build, or an SEO challenge — the Social Animal team would love to hear from you.

Get in touch →