Cumulative Layout Shift (CLS): Definition, Causes, Fixes
CLS is a Core Web Vital measuring unexpected visual shifts during page load. ≤ 0.1 = good. Caused by missing image dimensions, dynamic ads, late fonts.
What is Cumulative Layout Shift (CLS)?
Cumulative Layout Shift (CLS) is a Core Web Vital that measures the total amount of unexpected visual shifting that happens during a page's lifetime. Every time content jumps because an image loads late, an ad injects, or a font swaps, that's a layout shift — and it's frustrating for users (clicked the wrong button, lost their place in the article).
CLS is one of three Core Web Vitals (with LCP and INP) that Google uses as a search ranking signal. Pages with high CLS rank lower and feel broken, even if they technically work.
How CLS is measured
CLS = sum of layout shift scores during page lifetime. Each shift score = impact fraction × distance fraction:
- Impact fraction: how much of the viewport was affected by the shift
- Distance fraction: how far elements moved (relative to viewport)
| CLS | Rating |
|---|---|
| ≤ 0.1 | Good |
| 0.1 - 0.25 | Needs Improvement |
| > 0.25 | Poor |
Reported as the worst "session window" — a 5-second window of consecutive shifts.
What causes CLS
| Cause | Why | Fix |
|---|---|---|
| Images without width/height | Browser doesn't know how much space to reserve | Always set width + height attrs |
| Ads, embeds, iframes | Inject content of unknown size after page renders | Reserve space with explicit dimensions or skeleton |
| Web fonts loading late | FOUT/FOIT swaps fallback for web font, sizes differ | font-display: optional or size-adjust |
| Dynamic content above existing | Content injected at top pushes everything down | Append below, not above; or reserve space |
| Animations that change layout | Animating height/width moves siblings | Use transform (doesn't affect layout) |
| Cookie/GDPR banners appearing | Top banner pushes content down | Position fixed, or reserve space |
Common CLS fixes
1. Always specify image dimensions
<!-- BAD: causes CLS -->
<img src="hero.jpg" alt="Hero">
<!-- GOOD: aspect ratio reserved -->
<img src="hero.jpg" alt="Hero" width="1200" height="675">
<!-- For responsive images, browser computes aspect ratio -->2. Reserve space for ads/embeds
<!-- Ad slot with reserved height -->
<div class="ad-slot" style="min-height: 250px;">
<!-- Ad fills it -->
</div>3. Optimize web fonts
@font-face {
font-family: 'Inter';
src: url('inter.woff2') format('woff2');
font-display: optional; /* No swap */
size-adjust: 95%; /* Match fallback metrics */
}4. Use transform, not layout properties
/* BAD: animating height triggers reflow */
.menu { transition: height 0.3s; }
/* GOOD: transform doesn't affect layout */
.menu { transition: transform 0.3s; transform: translateY(0); }5. Avoid late-injected top content
<!-- Don't insert above existing content -->
<div id="banner"></div> <!-- empty placeholder -->
<main>...</main>
// JS: only fill banner; don't push main down
document.getElementById('banner').textContent = 'Welcome!';Measuring CLS
| Tool | Type |
|---|---|
| Lighthouse / PageSpeed Insights | Lab + field |
| Chrome DevTools Performance Panel | Lab, frame-by-frame |
| Web Vitals Chrome extension | Field, real browsing |
| web-vitals.js library | Programmatic, RUM |
| Google Search Console CWV report | Field, your real users |
| CrUX Dashboard | Field, public dataset |
// Capture CLS in JS
import { onCLS } from 'web-vitals';
onCLS((metric) => {
console.log('CLS:', metric.value);
// Send to RUM backend
});CLS best practices
- Always set width/height on images and videos. Browser computes aspect ratio.
- Reserve space for everything that loads late. Ads, embeds, iframes, lazy-loaded content.
- Use
font-display: optionalfor non-critical fonts. Avoids FOUT. - Match fallback font metrics with
size-adjust. Reduces font swap shift. - Transform animations, not layout.
transform+opacityare GPU-cheap and CLS-free. - Skeleton screens. Show placeholder of same dimensions as final content.
- Don't inject above existing content. Append, don't prepend.
- Test on slow devices. CLS is worse with slow loading; test on throttled mobile.
Common CLS pitfalls
- Responsive image without aspect ratio. Browser doesn't know height until loaded.
- Web fonts that resize text. Fallback Arial vs custom font with different metrics.
- Lazy-loaded ads with no reserved space. Ad pops in mid-scroll, jumps content.
- Click handlers on shifting elements. User clicks "Subscribe", button shifts, "Buy" takes the click.
- Auto-rotating carousels. Each rotation can shift adjacent content.
- Late-loading hero image. Container collapses then expands when image arrives.
FAQ: Cumulative Layout Shift
What's a good CLS score?
≤ 0.1 is good. 0.1-0.25 needs improvement. > 0.25 is poor.
Does CLS affect SEO?
Yes — it's one of three Core Web Vitals Google uses as a ranking signal. High CLS = lower ranking on competitive queries.
How do I fix images causing CLS?
Always set width and height attributes (or use aspect-ratio CSS). Browser reserves the right amount of space before image loads.
Do scrolling and user interactions count toward CLS?
No. Shifts within 500ms of user input are excluded. Only unexpected shifts count.
How does font-display affect CLS?
font-display: swap shows fallback then swaps — causes shift. optional shows fallback only if web font isn't ready quickly. Use optional + size-adjust for best CLS.
Can I have CLS = 0?
Possible but rare. Most sites with ads or dynamic content have some shift. Aim for < 0.1.
What is "session window" in CLS?
5-second window of consecutive shifts. CLS reports the largest such window's total shift, not all shifts summed across the entire page lifetime.
Catch CLS regressions with LoadFocus
LoadFocus runs Lighthouse audits from 25+ regions and tracks CLS over time, alerting on regressions before they hurt SEO. Sign up free at loadfocus.com/signup.
Related LoadFocus Tools
Put this concept into practice with LoadFocus — the same platform that powers everything you just read about.