Quick answer: Cumulative Layout Shift measures how much the page unexpectedly moves while loading. A good CLS is 0.1 or less. Fix it by setting explicit width and height on images and embeds, reserving space for ads and dynamically injected content, and preloading fonts so text does not reflow.
CLS measures unexpected layout movement during page load. It's one of Google's three Core Web Vitals, a stable, predictable page ranks better and converts better.
TL;DR
- What: Sum of all unexpected layout shifts during the page's lifecycle.
- Target: Under 0.1 (the "Good" threshold).
- Top three fixes: set explicit
width/heighton images, reserve space for ads/embeds, avoid inserting content above existing content.
What is Cumulative Layout Shift (CLS)?
Every time a visible element jumps to a new position without user input, CLS increases. The metric is impact fraction × distance fraction, summed over the page session. A button that moves down 100px right before the user clicks counts heavily; a tiny shift in a far corner barely registers.
What's a good CLS score?
| Range | Verdict |
|---|---|
| ≤ 0.1 | Good |
| 0.1–0.25 | Needs improvement |
| > 0.25 | Poor |
What causes Cumulative Layout Shift?
- Images without dimensions, the browser can't reserve space until it downloads them
- Web fonts swapping (FOUT/FOIT) and reflowing text
- Ads, embeds, iframes without reserved containers
- Dynamically injected content above existing content (banners, cookie notices, A/B test variants)
- Animations using
top/leftinstead oftransform
How do I fix Cumulative Layout Shift?
1. Always set width and height on images
<!-- WRONG: no dimensions, layout shifts when image loads -->
<img src="/photo.jpg" alt="…" />
<!-- RIGHT: dimensions reserve the aspect ratio -->
<img src="/photo.jpg" width="1200" height="800" alt="…" />
Modern browsers compute aspect-ratio automatically from these attributes, the slot is reserved before download finishes.
2. Reserve space for ads, embeds, iframes
.ad-slot {
min-height: 250px; /* reserve whatever the typical ad height is */
width: 100%;
}
If you don't know the ad height in advance, use a placeholder with a typical aspect ratio.
3. Avoid inserting content above existing content
Don't push existing content down. If you must inject a cookie banner or promo bar at the top of the viewport, either:
- Reserve its space ahead of time with a CSS
min-heighton<body> - Position it
fixedover the content rather than displacing it
4. Stabilize web fonts
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: optional; /* or swap with size-adjust */
size-adjust: 100%;
}
Or preload the font so it lands before FCP:
<link rel="preload" as="font" type="font/woff2" href="/fonts/inter.woff2" crossorigin />
5. Animate transform, not layout properties
/* WRONG: triggers layout */
.menu { left: 0; transition: left 0.3s; }
/* RIGHT: uses GPU compositing, no layout */
.menu { transform: translateX(0); transition: transform 0.3s; }
How do I verify CLS is fixed?
- Re-run Lighthouse, CLS should be < 0.1.
- Test on slow 3G in DevTools to catch font/image shifts that don't appear on fast networks.
- Check Chrome DevTools → Performance → Experience panel for individual shift events.
Related audits
- Largest Contentful Paint (LCP), load speed
- Total Blocking Time (TBT), main-thread blocking
- Image alt attributes, accessibility on images
Audit your URL at https://lighthouse-md.com.
Audit your page now
Paste your URL, get scores plus a CLAUDE.md plan for Claude Code.