If your Core Web Vitals report is flashing red because of Cumulative Layout Shift, you’re not alone. CLS is one of the trickiest Core Web Vitals to debug because it often happens fast, invisibly, or only on specific viewports. The good news: every layout shift has a cause, and every cause has a fix.
This guide is a diagnostic walkthrough. We’ll use Chrome DevTools and Lighthouse to identify exactly what is shifting on your page, then fix each issue with real code. By the end, you’ll have a repeatable checklist to fix layout shift CLS problems on any project.
What Counts as a “Good” CLS Score in 2026
Google still uses the same CLS thresholds that have been in place since the metric stabilized. Here’s where you need to land:
| CLS Score | Rating | Action Needed |
|---|---|---|
| 0.00 to 0.10 | Good | Maintain |
| 0.10 to 0.25 | Needs Improvement | Optimize soon |
| Above 0.25 | Poor | Fix immediately |
Step 1: Diagnose CLS with Chrome DevTools
Before writing a single line of CSS, you need to see what is actually shifting. Open your page in an Incognito window (to avoid extensions polluting results) and follow this workflow:
- Open DevTools (F12 or Cmd+Option+I)
- Go to the Performance panel
- Check Web Vitals in the top toolbar
- Click the reload icon to record a fresh page load
- Look for red Layout Shift blocks in the Experience track
- Click any block to see Moved from and Moved to coordinates plus the affected DOM node
Bonus: Highlight Shifts in Real Time
In DevTools, press Cmd/Ctrl+Shift+P, type Rendering, and enable Layout Shift Regions. Now every shift will flash blue on screen as it happens. This is the fastest way to spot the culprit visually.
Step 2: Run Lighthouse for a Prioritized List
The Lighthouse panel in Chrome DevTools gives you the Avoid large layout shifts diagnostic, which lists every shifting element ranked by contribution. Run Lighthouse in mobile mode first since most CLS issues surface there.
The Top 6 Causes of CLS and How to Fix Each One
1. Images Without Width and Height Attributes
This is the number one CLS offender. When the browser doesn’t know an image’s dimensions, it allocates zero space, then jumps the layout once the image loads.
The Fix: Always set explicit width and height attributes. Modern browsers use these to calculate aspect ratio automatically.
<!-- Bad -->
<img src="/hero.jpg" alt="Hero">
<!-- Good -->
<img src="/hero.jpg" alt="Hero" width="1200" height="600">
For responsive images, pair this with CSS:
img {
max-width: 100%;
height: auto;
}
2. Embeds, Iframes, and Ads Without Reserved Space
YouTube embeds, social widgets, and especially ad slots are notorious for shifting content when they hydrate. Reserve space using CSS aspect-ratio or a min-height container.
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
}
.ad-slot {
min-height: 250px;
min-width: 300px;
}
3. Web Fonts Causing FOUT or FOIT
When a fallback font swaps to a custom font with different metrics, every line of text reflows. This is one of the sneakiest sources of CLS.
The Fix: Use the size-adjust, ascent-override, and descent-override descriptors in @font-face to match fallback metrics, or use Google’s font-display: optional when you can tolerate the fallback.
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap;
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
}
Also preload critical fonts:
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
4. Dynamically Injected Content Above Existing Content
Cookie banners, newsletter popups, GDPR notices, and “app available” prompts that push content down are massive CLS triggers.
The Fix: Use position: fixed or position: sticky overlays so they don’t reflow the document. If you must insert in flow, reserve the space with a placeholder before render.
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 9999;
}
5. CSS Animations on Layout Properties
Animating top, left, width, or height triggers layout and is counted as CLS.
The Fix: Animate transform and opacity instead. They run on the compositor and don’t trigger layout shifts.
/* Bad */
.menu { transition: left 0.3s ease; }
/* Good */
.menu { transition: transform 0.3s ease; }
.menu.open { transform: translateX(0); }
6. Lazy-Loaded Components Without Skeletons
React, Vue, or Svelte components that fetch data and render late will shift everything below them.
The Fix: Render a skeleton placeholder of the same dimensions while loading.
function ProductCard({ data, loading }) {
if (loading) {
return <div style={{ height: 320, width: 240 }} className="skeleton" />;
}
return <div className="card">{data.title}</div>;
}
Step 3: Validate the Fix
After applying changes, validate using this checklist:
- Re-run Lighthouse in mobile mode and confirm CLS is below 0.10
- Use the PageSpeed Insights tool to compare lab vs field data
- Check the Core Web Vitals report in Google Search Console after 28 days for real user data
- Test on a slow 3G throttled connection in DevTools to expose font and image issues
- Test multiple viewports: 360px, 768px, 1280px
Quick Reference: CLS Fix Checklist
| Cause | Fix |
|---|---|
| Images without dimensions | Add width and height attributes |
| Embeds and ads | Use aspect-ratio or min-height |
| Web font swap | Preload + size-adjust descriptors |
| Injected banners | Position fixed or sticky |
| Animations | Use transform and opacity |
| Async components | Render dimension matched skeletons |
FAQ
What is a layout shift in simple terms?
A layout shift happens when a visible element on the page changes its position between two frames without user interaction. If a button moves down because an image loaded above it, that’s a layout shift.
Does CLS only count above-the-fold shifts?
No. CLS measures shifts anywhere on the page during the entire lifecycle, but only shifts that affect content currently in the viewport count. Shifts caused by user input within 500ms are excluded.
Why does my CLS look fine in Lighthouse but bad in Search Console?
Lighthouse provides lab data measured in a controlled environment. Search Console reports field data from real users on real devices. Field CLS captures shifts caused by interactions, slow networks, and varied screen sizes that lab tests miss.
Can lazy-loading images cause CLS?
Only if the image lacks width and height attributes. With dimensions set, lazy-loading is safe and recommended for performance.
How long until Search Console reflects my CLS fix?
The Core Web Vitals report uses a rolling 28-day window of CrUX data, so expect to wait three to four weeks to see significant improvement after deploying fixes.
Is CLS still important in 2026?
Yes. CLS remains one of the three Core Web Vitals Google uses as a ranking signal, alongside LCP and INP. A poor CLS score still hurts rankings and user experience.
Final Thoughts
Fixing CLS is rarely about one big change. It’s about systematically eliminating small shifts: a missing height here, a font swap there, a banner that pushes content down. Use Chrome DevTools and Lighthouse to identify each offender, apply the targeted fix from this checklist, and validate with field data. Your users (and your rankings) will thank you.

