Cumulative Layout Shift (CLS) — visual stability and how to control it
CLS is a Core Web Vital measuring unexpected layout shifts during the page's life — content jumping as images, ads, or fonts load. Good CLS is ≤ 0.1. Control it by always reserving space: width/height (or aspect-ratio) on media, min-height for async content/ads, font-display strategy + size-adjust to limit FOUT shift, and never inserting content above existing content.
Cumulative Layout Shift (CLS) is a Core Web Vital that measures visual stability — how much content unexpectedly jumps around as the page loads. You've felt it: you go to tap a button and an image loads above it, shoving the button down, and you tap an ad instead.
How it's scored
CLS = sum of layout shift scores for unexpected shifts, where each score = impact fraction × distance fraction. (Modern CLS uses a "session window" so a long-lived page isn't unfairly penalized.)
| CLS | Rating |
|---|---|
| ≤ 0.1 | Good |
| 0.1 – 0.25 | Needs improvement |
| > 0.25 | Poor |
Shifts in response to user interaction (within ~500ms) are excluded — those are expected.
The main causes & fixes
1. Images/videos without dimensions
<!-- ❌ space isn't reserved; layout jumps when it loads -->
<img src="hero.jpg" />
<!-- ✅ reserve the box -->
<img src="hero.jpg" width="800" height="600" />Or with CSS aspect-ratio: 16 / 9. With width/height attributes, the browser computes the aspect ratio and reserves space before the image arrives.
2. Ads, embeds, iframes — reserve a min-height box for the slot so it doesn't push content when it fills.
3. Web fonts (FOUT/FOIT) — when a custom font swaps in, text reflows if its metrics differ. Mitigate with:
font-display: optionalorswap(deliberate choice),size-adjust/ascent-overrideon@font-faceto match fallback metrics,- preloading the font.
4. Dynamically injected content — banners, "you have new messages" bars inserted above existing content. Either reserve space, or insert below the fold / overlay it instead of pushing.
5. Animating layout properties — animate transform/opacity, not top/height/margin.
How to measure it
- Lab: Lighthouse, WebPageTest, Chrome DevTools Performance panel (it flags shift regions).
- Field (real users): the
web-vitalsJS library, the Layout Instability API, Chrome UX Report (CrUX) — field data is what Google ranks on.
The one rule
Always reserve space for anything that loads or appears asynchronously. Images, ads, fonts, lazy content — give it a box up front.
Senior framing
The senior answer frames CLS as "reserve space proactively" and shows breadth across all the causes — not just "set image dimensions." Knowing aspect-ratio, size-adjust for font-swap shifts, the session-window scoring change, and that field data (CrUX) is what actually affects SEO — that's the depth interviewers want.
Follow-up questions
- •How does the `aspect-ratio` property help with CLS?
- •How do web fonts cause layout shift and how do you fix it?
- •Why are user-initiated shifts excluded from CLS?
- •What's the difference between lab and field CLS data?
Common mistakes
- •Omitting width/height on images.
- •Injecting banners above existing content.
- •Ignoring font-swap reflow.
- •Optimizing only lab CLS while field CLS (CrUX) stays poor.
Edge cases
- •Lazy-loaded content below the fold still shifts if not space-reserved.
- •CLS can be near-zero in lab but bad in field due to slow networks/ads.
- •Single-page apps: route transitions can shift content unexpectedly.
Real-world examples
- •News sites with ads, image-heavy product pages, sites with custom web fonts.