Cumulative Layout Shift (CLS)

Master the art of preventing layout shifts to improve visual stability, pass Core Web Vitals, and provide a better user experience.

What is Cumulative Layout Shift?

Cumulative Layout Shift (CLS) measures how much the layout shifts during page loading. Unexpected layout shifts occur when elements change position, causing a poor user experience and potentially leading to users clicking the wrong elements.

A low CLS score means your page elements stay put as they load, providing a stable and predictable experience for your users. This is crucial for maintaining user trust and engagement.

Common Causes of Layout Shifts

  • Images without dimensions loading
  • Ads, embeds, and iframes without reserved space
  • Fonts causing flash of unstyled text (FOUT)
  • Dynamic content insertion above existing content
CLS Performance Chart

CLS Scoring Criteria

Understand what constitutes good, needs improvement, and poor CLS scores

Good

≤ 0.1

Your page has excellent visual stability

⚠️

Needs Improvement

0.1 - 0.25

Some layout shifts may affect user experience

Poor

> 0.25

Significant layout shifts affect usability

How CLS is Calculated

CLS Formula

CLS = Impact Fraction × Distance Fraction
  • Impact Fraction: Percentage of viewport affected
  • Distance Fraction: How far elements moved

Example Calculation

If an element:

  • • Affects 50% of viewport (0.5 impact)
  • • Moves 25% of viewport height (0.25 distance)
  • CLS = 0.5 × 0.25 = 0.125

CLS Examples: Before & After

See how proper implementation prevents layout shifts

❌ Poor Implementation (High CLS)

Problem: Image without dimensions

<!-- This causes layout shift when the image loads --> <img src="/cls/hero.jpg" alt="Hero image"> <!-- This text will jump when the image loads --> <p>This text will move down when the image above loads</p>

Result: The browser doesn't know how much space to reserve, causing content below to shift when the image loads.

Problem: Dynamic ads without reserved space

<div id="ad-container"></div> <script> // Ad loads after page content, pushing everything down fetch('/api/ad').then(response => { document.getElementById('ad-container').innerHTML = response.adHTML; }); </script>

Result: The ad container has no height initially, causing a major layout shift when the ad loads.

✅ Good Implementation (Low CLS)

Solution: Image with dimensions

<!-- Browser reserves space, preventing layout shift --> <img src="/cls/hero.jpg" alt="Hero image" width="1200" height="600" style="width: 100%; height: auto;" > <!-- This text stays in place --> <p>This text stays in place when the image loads</p>

Result: The browser reserves 1200x600px space, preventing any layout shift when the image loads.

Solution: Reserved space for ads

<div id="ad-container" style="min-height: 250px; background: #f0f0f0;"> <div class="ad-placeholder">Advertisement</div> </div> <script> // Ad loads into reserved space, no layout shift fetch('/api/ad').then(response => { const container = document.getElementById('ad-container'); container.innerHTML = response.adHTML; container.style.minHeight = 'auto'; // Remove placeholder height }); </script>

Result: The ad container has reserved space, preventing layout shift when the ad loads.

CLS Optimization Techniques

Proven strategies to eliminate layout shifts

🖼️ Always Specify Image Dimensions

The most common cause of layout shifts is images loading without reserved space. Always specify width and height attributes to prevent this issue.

Best Practices:

  • Use width and height attributes
  • Set CSS aspect-ratio for responsive images
  • Use CSS to maintain aspect ratio
  • Preload critical images

Code Examples:

<!-- ❌ Bad: No dimensions specified --> <img src="/cls/hero.jpg" alt="Hero image"> <!-- ✅ Good: Dimensions specified --> <img src="/cls/hero.jpg" alt="Hero image" width="1200" height="600"> <!-- ✅ Better: Responsive with aspect ratio --> <img src="/cls/hero.jpg" alt="Hero image" width="1200" height="600" style="width: 100%; height: auto; aspect-ratio: 16/9;" >

📢 Reserve Space for Ads and Embeds

Ads, embeds, and iframes often load after the initial page content, causing major layout shifts. Reserve space for these elements to prevent CLS issues.

Implementation:

  • Set fixed container heights
  • Use min-height for flexible sizing
  • Add loading placeholders
  • Handle different ad sizes gracefully

Code Example:

<!-- Ad container with reserved space --> <div class="ad-container" style="min-height: 250px;"> <div class="ad-placeholder"> <div class="spinner"></div> <p>Loading advertisement...</p> </div> </div> <style> .ad-container { min-height: 250px; background: #f5f5f5; display: flex; align-items: center; justify-content: center; border: 1px solid #ddd; } .ad-placeholder { text-align: center; color: #666; } .spinner { border: 2px solid #f3f3f3; border-top: 2px solid #3498db; border-radius: 50%; width: 20px; height: 20px; animation: spin 1s linear infinite; margin: 0 auto 10px; } </style>

🔤 Optimize Web Fonts

Web fonts can cause layout shifts when they load after the initial text rendering. This is known as Flash of Unstyled Text (FOUT) or Flash of Invisible Text (FOIT).

Font Optimization Techniques:

  • Use font-display: swap
  • Preload critical fonts
  • Use fallback fonts with similar metrics
  • Subset fonts to reduce file size

Font CSS:

/* Optimized font loading */ @font-face { font-family: 'CustomFont'; src: url('font.woff2') format('woff2'); font-display: swap; font-weight: 400; font-style: normal; } /* Fallback font with similar metrics */ body { font-family: 'CustomFont', 'Arial', sans-serif; } /* Preload critical fonts */ <link rel="preload" href="/cls/font.woff2" as="font" type="font/woff2" crossorigin>

🔄 Handle Dynamic Content Carefully

Dynamic content that loads after the initial page render can cause significant layout shifts. Plan for this content by reserving space and handling updates gracefully.

Best Practices:

  • Reserve space for dynamic content
  • Avoid inserting content above existing content
  • Use CSS Grid for flexible layouts
  • Animate content changes smoothly

Implementation:

<!-- Reserve space for dynamic content --> <div id="dynamic-content" style="min-height: 200px;"> <div class="loading-skeleton"></div> </div> <script> // Load content into reserved space fetch('/api/content') .then(response => response.json()) .then(data => { const container = document.getElementById('dynamic-content'); container.innerHTML = data.html; container.style.minHeight = 'auto'; // Remove placeholder height }); </script>

Best Tools for CLS Optimization

Recommended tools to help you achieve excellent CLS scores

NitroPack

All-in-one optimization platform with advanced font optimization and layout stability features.

CLS Optimization
Learn More
🎯

FlyingPress

WordPress-focused plugin with excellent font and layout optimization capabilities.

Font Optimization
Learn More
📊

GTmetrix Pro

Professional performance testing with detailed CLS analysis and layout shift visualization.

CLS Analysis
Learn More

Ready to Fix Your CLS Issues?

Start optimizing your Cumulative Layout Shift today and provide a more stable, user-friendly experience. Test your current CLS score and get personalized recommendations.