You're reading an article, about to tap a link, and suddenly the page shifts. You tap an ad instead. That's Cumulative Layout Shift (CLS) - one of Google's three Core Web Vitals alongside LCP and INP.
A good CLS score is under 0.1. Anything over 0.25 is poor. Unlike LCP, CLS is cumulative - every shift during the page's lifetime adds up.
What causes layout shifts
A layout shift happens when visible content moves without user interaction. The browser calculates how much of the viewport was affected and how far things moved.
Common causes:
- Images without dimensions
- Ads or embeds that load late
- Web fonts causing text to reflow
- Dynamic content inserted above existing content
The key insight: shifts only count if they're unexpected. If a user clicks a button and content expands, that's fine. If content moves on its own, that's a problem.
Find your layout shifts
Option 1: DevTools
- Open Chrome DevTools
- Go to Performance tab
- Enable "Web Vitals" in settings
- Record a page load
- Look for red "Layout Shift" markers
Option 2: Layout Shift Debugger
Add this to your console to highlight shifts in real-time:
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Layout shift:', entry);
}
}).observe({ type: 'layout-shift', buffered: true });
Option 3: PageSpeedFix
Run your URL through PageSpeedFix - we identify the specific elements causing shifts and prioritize them by impact.
The 4 main fixes
1. Always set image dimensions
When an image loads, the browser needs to know how much space to reserve. Without dimensions, it reserves zero space, then shifts everything when the image appears.
What to do:
Always include width and height attributes:
<img
src="photo.jpg"
width="800"
height="600"
alt="Photo"
/>
For responsive images, use CSS aspect-ratio:
img {
width: 100%;
height: auto;
aspect-ratio: 4 / 3;
}
In Next.js, the Image component handles this automatically:
<Image src="/photo.jpg" width={800} height={600} alt="Photo" />
In Nuxt:
<NuxtImg src="/photo.jpg" width="800" height="600" />
2. Reserve space for ads and embeds
Ads are notorious for causing layout shifts because they load asynchronously and often have variable heights.
What to do:
Create a container with minimum dimensions:
.ad-container {
min-height: 250px; /* Standard ad height */
background: #f0f0f0;
}
For embeds like YouTube or Twitter, use aspect-ratio containers:
.video-embed {
aspect-ratio: 16 / 9;
width: 100%;
}
3. Handle web fonts properly
When a web font loads, text often reflows. The browser might show invisible text (FOIT) or swap from a fallback font (FOUT), both causing shifts.
What to do:
Use font-display: optional for non-critical fonts:
@font-face {
font-family: 'Custom Font';
src: url('/font.woff2') format('woff2');
font-display: optional;
}
For critical fonts, preload them:
<link
rel="preload"
href="/font.woff2"
as="font"
type="font/woff2"
crossorigin
/>
Or use a font with similar metrics to your fallback to minimize the shift.
4. Don't insert content above existing content
If you load a banner, notification, or any content that pushes existing content down, that's a layout shift.
What to do:
- Reserve space for dynamic content upfront
- Add new content below the viewport, not above
- Use transforms for animations instead of changing layout properties
/* Bad - causes layout shift */
.notification {
margin-top: 20px; /* Pushes content down */
}
/* Good - no layout shift */
.notification {
transform: translateY(20px);
}
Quick checklist
- Do all images have
widthandheight(oraspect-ratio)? - Do ads/embeds have reserved space?
- Are fonts preloaded or using
font-display: optional? - Is dynamic content inserted below the viewport?
- Are animations using
transforminstead of layout properties?
Frequently Asked Questions
What is a good CLS score?
A good CLS score is under 0.1. Scores between 0.1-0.25 need improvement, and anything over 0.25 is considered poor. Unlike other metrics, CLS is unitless - it's a calculation of how much content shifted and how far.
What causes Cumulative Layout Shift?
The most common causes are: images without dimensions, late-loading ads or embeds, web fonts that reflow text, and dynamic content inserted above existing content. Each of these causes visible elements to move unexpectedly.
Does CLS affect SEO?
Yes. CLS is one of Google's three Core Web Vitals and affects search rankings. Beyond SEO, poor CLS frustrates users - especially when they try to click something and it moves.
How do I fix CLS from images?
Always set width and height attributes on images, or use CSS aspect-ratio. This lets the browser reserve space before the image loads. Framework image components like Next.js <Image> and Nuxt <NuxtImg> handle this automatically.
How do I fix CLS from fonts?
Use font-display: optional or font-display: swap in your font-face declarations. Better yet, preload critical fonts or use a tool like Fontaine that creates fallback fonts with matching metrics.
What's next
Want to see exactly which elements are causing your layout shifts? Run your site through PageSpeedFix for a prioritized breakdown with framework-specific fixes.