Cumulative Layout Shift (CLS)

Last updated July 17, 2025

CLS is a Core Web Vital that measures visual stability. It tracks how often unexpected layout shifts happen, which can disrupt the user experience — like making someone accidentally click the wrong button or lose their place while reading.

Unexpected shifts often happen due to:

  • Images or videos without defined dimensions.
  • Dynamically loaded content (like ads or widgets).
  • Fonts that render larger or smaller than their fallback version.

These shifts can be more noticeable in production than in development due to factors like cache differences or slow API responses.


How CLS is Measured

CLS calculates the largest burst of layout shift scores during a page's lifecycle.

A layout shift happens when a visible element changes position between two frames.

The score is based on two factors:

  1. Impact fraction: The portion of the viewport affected by the shift.
  2. Distance fraction: How far elements move, relative to the viewport size.

Formula:

  1. CLS score = impact fraction × distance fraction

For example, if an element takes up 50% of the viewport and moves down by 25%:

  1. CLS score = 0.5 × 0.25 = 0.125

What is a Good CLS Score?

  • Good: 0.1 or less
  • Needs improvement: 0.1 – 0.25
  • Poor: Above 0.25
Score Scale
0.1
0.25

Aim for a score of 0.1 or lower at the 75th percentile across mobile and desktop users.


Measuring CLS

You can measure CLS using these tools:

Field Tools:

  • Chrome User Experience Report
  • PageSpeed Insights
  • Search Console (Core Web Vitals report)
  • web-vitals JavaScript library

Lab Tools:

  • Chrome DevTools
  • Lighthouse
  • WebPageTest

Or directly with JavaScript:

  1. new PerformanceObserver((entryList) => {
  2.   for (const entry of entryList.getEntries()) {
  3.     console.log('Layout shift:', entry);
  4.   }
  5. }).observe({ type: 'layout-shift', buffered: true });

Dealing with CLS Edge Cases

  • Background tabs: CLS should only count if the page was in the foreground.
  • Back/forward cache: CLS should reset on page restores.
  • Iframes: Layout shifts in iframes count toward CLS, but the API might not catch them.

To avoid handling all these nuances manually, you can use the web-vitals library:

  1. import { onCLS } from 'web-vitals';
  2. onCLS(console.log);

How to Reduce CLS

1. Set dimensions for images, videos, and iframes

To reduce layout shift, always reserve space for media content. This prevents elements from jumping around when images or iframes load.

✓ Basic solution: Set explicit width and height attributes. Browsers will reserve that exact space before loading.

  1. Banner

✓ Responsive solution (recommended for fluid layouts):

Reserve space using the padding-top trick, which preserves aspect ratio and avoids CLS, even when the image is responsive.

  1. AI tools for long-form bloggers at Copy.ai
  2.          class="scr"
  3.          src="//domain.com/images/my-image.jpg"
  4.          title="Copy.ai has a built-in editor">
    • padding-top: 58.2301% preserves a 500×291 aspect ratio
    • position: absolute on the image ensures it fills the reserved container
    • width: 100% inside a max-width-constrained wrapper ensures responsiveness

    2. Reserve space for dynamic content (like ads or embeds)

    Use CSS to allocate a fixed-height container for content that may load late.

    3. Preload fonts to avoid Flash of Unstyled Text (FOUT)

    Use to fetch fonts early and prevent layout shifts.

    4. Use CSS transforms instead of top/left/width for animations

    Animating layout properties causes reflows. Use GPU-friendly transforms instead.

    1. /* BAD (triggers layout reflow): */
    2. .moving-box {
    3.   position: relative;
    4.   animation: moveDown 1s ease-in-out;
    5. }
    6. @keyframes moveDown {
    7.   0% { top: 0; }
    8.   100% { top: 100px; }
    9. }
    1. /* GOOD (no reflow, GPU-friendly): */
    2. .moving-box {
    3.   animation: slideDown 1s ease-in-out;
    4. }
    5. @keyframes slideDown {
    6.   0% { transform: translateY(0); }
    7.   100% { transform: translateY(100px); }
    8. }

    5. Show placeholders or loaders for delayed content

    Use skeleton loaders or fixed placeholders to avoid shifts while content loads.

    For detailed insights, run a Lighthouse audit to find and fix layout shift issues.