Cumulative Layout Shift (CLS)
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:
- Impact fraction: The portion of the viewport affected by the shift.
- Distance fraction: How far elements move, relative to the viewport size.
Formula:
- CLS score = impact fraction × distance fraction
For example, if an element takes up 50% of the viewport and moves down by 25%:
- 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

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:
- new PerformanceObserver((entryList) => {
- for (const entry of entryList.getEntries()) {
- console.log('Layout shift:', entry);
- }
- }).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:
- import { onCLS } from 'web-vitals';
- 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.
✓ 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.
- class="scr"
- src="//domain.com/images/my-image.jpg"
- title="Copy.ai has a built-in editor">
padding-top: 58.2301%
preserves a 500×291 aspect ratioposition: absolute
on the image ensures it fills the reserved containerwidth: 100%
inside amax-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.
- @font-face {
- font-family: 'Inter';
- src: url('//cdn.testdom.io/fonts/inter-400.woff2') format('woff2');
- font-weight: 400;
- font-display: swap;
- }
4. Use CSS transforms instead of top/left/width for animations
Animating layout properties causes reflows. Use GPU-friendly transforms instead.
- /* BAD (triggers layout reflow): */
- .moving-box {
- position: relative;
- animation: moveDown 1s ease-in-out;
- }
- @keyframes moveDown {
- 0% { top: 0; }
- 100% { top: 100px; }
- }
- /* GOOD (no reflow, GPU-friendly): */
- .moving-box {
- animation: slideDown 1s ease-in-out;
- }
- @keyframes slideDown {
- 0% { transform: translateY(0); }
- 100% { transform: translateY(100px); }
- }
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.