Cumulative Layout Shift (CLS)
What is Cumulative Layout Shift (CLS)?
Cumulative Layout Shift (CLS) is one of Google's three SEO Metrics metrics and measures the visual stability of a web page. CLS quantifies how often unexpected layout shifts occur during the entire lifecycle of a page.
Definition and Importance
CLS measures the sum of all layout shifts that are not caused by user interactions. These shifts occur when visible elements change their position after they have already been rendered.
Key Metrics:
- Good: CLS ≤ 0.1
- Needs Improvement: 0.1 < CLS ≤ 0.25
- Poor: CLS > 0.25
How is CLS calculated?
Calculation Formula
CLS is calculated using the following formula:
CLS = Σ (Shift Area × Distance Fraction)
Impact Fraction: Proportion of the viewport affected by the layout shift
Distance Fraction: Largest distance an element was shifted (relative to viewport size)
Measurement Timing
CLS is measured throughout the entire lifetime of a page, not just on initial load. This includes:
- Initial loading
- Dynamic content
- User interactions
- Asynchronous resources
Common Causes of Layout Shifts
1. Images without Dimensions
Problem: Images are loaded without fixed width and height, causing the layout to jump when the image loads.
Solution:
<!-- Bad -->
<img src="image.jpg" alt="Description">
<!-- Good -->
<img src="image.jpg" alt="Description" width="800" height="600">
2. Dynamically Inserted Content
Problem: Content is added after initial loading and shifts existing elements.
Examples:
- Ad banners
- Social media widgets
- Comments
- Recommendations
3. External Fonts without Fallback
Problem: Web fonts are loaded asynchronously and replace fallback fonts, causing text sizes and spacing to change.
Solution:
@font-face {
font-family: 'Custom Font';
font-display: swap; /* Prevents FOIT */
src: url('font.woff2') format('woff2');
}
4. Asynchronous Scripts
Problem: JavaScript loads content asynchronously and shifts the layout in the process.
Examples:
- Analytics tracking
- Chat widgets
- Cookie banners
CLS Optimization Strategies
1. Optimize Images
Reserved space for images:
<div class="image-container" style="aspect-ratio: 16/9;">
<img src="image.jpg" alt="Description" loading="lazy">
</div>
CSS Solution:
.image-container {
width: 100%;
height: 0;
padding-bottom: 56.25%; /* 16:9 Aspect Ratio */
position: relative;
}
.image-container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
2. Optimize Web Fonts
Font-Display Strategies:
@font-face {
font-family: 'Custom Font';
font-display: swap; /* Faster text visibility */
src: url('font.woff2') format('woff2');
}
/* Define fallback font */
body {
font-family: 'Custom Font', Arial, sans-serif;
}
3. Prepare Dynamic Content
Reserved space for dynamic content:
<div class="content-area">
<div class="placeholder" style="height: 200px;">
<!-- Content will be inserted here -->
</div>
</div>
4. Use CSS Transformations
Instead of layout changes:
/* Bad - causes layout shift */
.element {
margin-top: 20px;
}
/* Good - uses transform */
.element {
transform: translateY(20px);
}
CLS Measurement and Monitoring
Tools for CLS Measurement
Using Chrome DevTools
Step-by-step guide:
- Open DevTools (F12)
- Select Performance tab
- Click Record button
- Load page and interact
- Press Stop
- Search for Layout Shifts in the timeline
Analyze Layout Shift Records:
- Cumulative Score: Total CLS value
- Sources: Cause of the shift
- Nodes: Affected DOM elements
Best Practices for CLS Optimization
1. Define Dimensions for All Media
<!-- Images -->
<img src="image.jpg" width="800" height="600" alt="Description">
<!-- Videos -->
<video width="800" height="450" controls>
<source src="video.mp4" type="video/mp4">
</video>
<!-- Iframes -->
<iframe src="embed.html" width="800" height="600"></iframe>
2. Use CSS Containers
.media-container {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%; /* 16:9 */
overflow: hidden;
}
.media-container img,
.media-container video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
3. Optimize Font Loading
<!-- Preload important fonts -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<!-- Font display swap -->
<style>
@font-face {
font-family: 'Custom Font';
font-display: swap;
src: url('font.woff2') format('woff2');
}
</style>
4. Prepare Dynamic Content
/* Placeholder for dynamic content */
.dynamic-content {
min-height: 200px;
background: #f5f5f5;
}
/* Skeleton Loading */
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
CLS Debugging Techniques
1. Layout Shift Debugging
// Enable CLS measurement
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
console.log('Layout Shift:', entry.value);
console.log('Sources:', entry.sources);
}
}
}).observe({type: 'layout-shift', buffered: true});
2. Visual Debugging Tools
Chrome DevTools:
- Rendering tab → Enable "Layout Shift Regions"
- Performance tab → Analyze Layout Shift Records
Browser Extensions:
- Web Vitals Extension
- Lighthouse CI
3. Automated Tests
// Puppeteer/Playwright Test
const { chromium } = require('playwright');
async function measureCLS() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
const cls = await page.evaluate(() => {
return new Promise((resolve) => {
let clsValue = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
resolve(clsValue);
}).observe({type: 'layout-shift', buffered: true});
});
});
console.log('CLS Score:', cls);
await browser.close();
}
Common CLS Errors and Solutions
1. Cookie Banner without Reserved Space
Problem: Cookie banner appears suddenly and shifts content.
Solution:
/* Reserve space for cookie banner */
body {
padding-bottom: 60px; /* Height of cookie banner */
}
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 60px;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.cookie-banner.show {
transform: translateY(0);
}
2. Lazy Load without Dimensions
Problem: Lazy-loaded images shift layout when loading.
Solution:
<div class="image-wrapper" style="aspect-ratio: 16/9;">
<img
src="placeholder.jpg"
data-src="real-image.jpg"
alt="Description"
loading="lazy"
class="lazy-image"
>
</div>
3. Dynamic Advertising
Problem: Ad banners are loaded asynchronously and shift content.
Solution:
<!-- Reserved space for advertising -->
<div class="ad-container" style="height: 250px; background: #f0f0f0;">
<div id="ad-slot"></div>
</div>
<script>
// Only load ad when container is visible
const adContainer = document.getElementById('ad-slot');
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadAd();
observer.disconnect();
}
});
observer.observe(adContainer);
</script>
CLS and SEO Impact
Ranking Factor since 2021
CLS has been an official Google ranking factor since May 2021 and is part of Core Web Vitals. Poor CLS values can lead to:
- Ranking losses in search results
- Reduced visibility in Google Search Console
- Poor user experience and higher bounce rate
- Loss of conversions and engagement
Mobile-Priority Indexing
Since Google uses mobile-first indexing, CLS is particularly important for:
- Mobile performance and user experience
- Mobile search rankings
- Core Web Vitals on mobile devices
CLS Monitoring Strategies
1. Continuous Monitoring
Tools for continuous monitoring:
- Google Search Console
- PageSpeed Insights API
- Custom analytics integration
- Real User Monitoring (RUM)
2. Alerts and Notifications
// CLS alert system
function checkCLS() {
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.value > 0.25) {
// Send alert
sendAlert('CLS Warning', `CLS: ${entry.value}`);
}
}
}).observe({type: 'layout-shift', buffered: true});
}
3. A/B Testing for CLS
Test design for CLS optimization:
- Variant A: Original layout
- Variant B: Optimized layout with reserved spaces
- Metrics: CLS, bounce rate, Goal Rate
Future of CLS Measurement
Extended CLS Metrics
Google is working on extended CLS metrics:
- CLS 2.0: Improved measurement of layout shifts
- Subframe CLS: Measurement in iframes
- Windowed CLS: Measurement in specific time windows
New Optimization Possibilities
CSS Container Queries:
@container (min-width: 300px) {
.content {
font-size: 1.2rem;
}
}
CSS Scroll Timeline:
@scroll-timeline scroll-timeline {
source: selector(#scroll-container);
orientation: vertical;
}
CLS Optimization Checklist
✅ Technical Checklist
- [ ] All images have fixed dimensions
- [ ] Web fonts use
font-display: swap - [ ] Dynamic content has reserved space
- [ ] CSS transformations instead of layout changes
- [ ] Lazy loading with aspect ratio
- [ ] Cookie banner without layout shift
- [ ] Advertising with fixed dimensions
✅ Monitoring Checklist
- [ ] Monitor Google Search Console
- [ ] Test PageSpeed Insights regularly
- [ ] Use Chrome DevTools Performance tab
- [ ] Implement Real User Monitoring
- [ ] Set up alerts for CLS thresholds
✅ Content Checklist
- [ ] All media files optimized
- [ ] Font loading strategy defined
- [ ] Dynamic content prepared
- [ ] Mobile-first approach implemented
- [ ] User experience prioritized