CLS Optimization Guide: Fix Layout Shifts That Hurt Rankings and Conversions
Your page loads. Text appears. The user starts reading. An ad loads—everything jumps down 200 pixels. The user clicks the wrong button. Frustration. Bounce.
This is Cumulative Layout Shift (CLS), and it's destroying your Core Web Vitals scores, your search rankings, and your conversion rates. Google measures it. Users feel it. And unlike other performance metrics, CLS is entirely preventable with the right technical approach.
Key Insight
CLS is entirely preventable with proper technical implementation. Unlike other performance metrics, you can achieve near-perfect scores by following the right optimization strategies.
This guide covers everything you need to optimize CLS: what it is, why it matters, how to measure it, and—most importantly—how to fix it systematically across your entire site. Whether you're handling this yourself or working with your development team, you'll find actionable solutions for every common cause of layout shifts.
For comprehensive technical SEO optimization that includes CLS as part of your site's overall user experience strategy, Digital Thrive's audits identify layout shift sources and provide implementation-ready solutions.
What is Cumulative Layout Shift (CLS)?
Cumulative Layout Shift measures visual stability—specifically, how much your page layout unexpectedly shifts during the loading process and user interaction. Think of CLS as a quantification of that frustrating experience: the jumpy page that makes you click the wrong button or lose your place while reading.
Definition in Plain Language
Understanding CLS in Simple Terms
CLS is the sum of all unexpected layout movements that occur during the entire lifetime of a page load. It specifically measures shifts that happen without user input—when you didn't scroll, click, or type. This distinction is critical because shifts that happen in response to your actions (opening a dropdown menu you clicked, for example) don't count toward CLS.
The more your page jumps around as it loads, the higher your CLS score. The more stable your page remains during loading, the lower the score. A perfect score of 0.0 means absolutely no unexpected shifts occurred.
The Technical Definition
From a technical standpoint, CLS is one of Google's three Core Web Vitals—a set of metrics that measure real-world user experience across loading performance, interactivity, and visual stability. Google introduced Core Web Vitals as a ranking factor in June 2021, meaning your CLS score directly influences search engine rankings.
The importance: CLS isn't just a nice-to-have metric. When your site has poor CLS, Google sees it as a signal of poor user experience, and your rankings suffer accordingly. More importantly, your users experience frustration—and frustrated users don't convert.
How CLS is Calculated: The Formula
Understanding CLS calculation helps you debug issues more effectively. CLS uses a specific mathematical formula:
CLS = Impact Fraction × Distance Fraction
Let's break down each component:
Impact Fraction represents the percentage of the viewport affected by a layout shift. If a banner appears at the top of the page that takes up 25% of the viewport height, that shift has an impact fraction of 0.25.
Distance Fraction represents how far elements moved, expressed as a fraction of the viewport height or width (whichever is larger). If content shifts down by half the viewport height, that's a distance fraction of 0.5.
The browser calculates this for each unexpected layout shift and sums them all together throughout the page's entire lifespan—including after the page appears "done loading." This is why a single large shift or many small shifts throughout the page can accumulate into a problematic CLS score.
Important distinction: Only unexpected shifts count. If you click a button and content expands as a result, that shift doesn't count toward CLS. If you scroll and an image loads and causes a shift, that doesn't count. Only shifts that happen without your input are measured.
CLS Score Thresholds: Good, Needs Improvement, Poor
Google's CLS Thresholds
Google defines specific thresholds for CLS scores, measured at the 75th percentile of page loads (meaning 75% of your users should achieve this score or better):
- Good: 0.1 or less
- Needs Improvement: 0.1 to 0.25
- Poor: Above 0.25
The critical number is 0.1. This is Google's "good" threshold and should be your target.
One nuance: Google measures at the 75th percentile, not the average. This matters because it means your worst-performing 25% of users matter for ranking purposes. A site that loads perfectly for most visitors but has severe CLS for mobile users on 3G connections will still be penalized.
Why CLS Matters (Beyond Rankings)
While CLS does affect rankings, the real business impact is much larger. Let's explore why CLS matters across three dimensions: SEO, conversion rates, and user experience.
SEO Impact: The Ranking Factor
Since June 2021, CLS has been an official Google ranking factor. However, it's worth noting that CLS is one of many ranking factors—content quality and relevance still matter far more than perfect Core Web Vitals.
The practical impact: a site with excellent content and poor CLS might still rank well. A site with poor content and perfect CLS will rank poorly. But two sites with similar content? The one with better Core Web Vitals (including CLS) will outrank the other.
Mobile-first indexing amplifies CLS's importance. Google crawls and ranks based primarily on mobile performance, and mobile CLS scores tend to be worse than desktop (slower networks, smaller viewports, touch precision requirements). If your mobile CLS is poor, your mobile rankings suffer—which matters because 60%+ of web traffic is mobile.
Technical SEO issues often correlate with CLS problems. Sites with render-blocking resources, missing image dimensions, and unoptimized font loading tend to have multiple technical problems, not just CLS. Fixing CLS often means addressing these broader technical issues, which improve crawlability, indexability, and overall site health.
Conversion Rate Impact: The Business Case
Conversion Rate Impact
This is the metric that should motivate you most: layout shifts directly erode user trust and decrease conversion rates.
When a page jumps while you're interacting with it, you subconsciously perceive the site as low-quality or poorly maintained. You might accidentally click the wrong button or link while a shift happens—frustrating, and a direct loss of sales. Studies across the industry show that sites with poor CLS consistently see lower conversion rates than sites with stable layouts.
Here's the cascade: User lands on your page → Page jumps as it loads → User thinks "this site is sketchy" or accidentally clicks wrong button → User leaves without converting. Repeat that thousands of times per month and you're losing substantial revenue.
Real-world observations show sites that fix CLS issues often see conversion rate improvements, even before considering the ranking benefits. For ecommerce sites, this can mean hundreds of thousands of dollars in additional revenue annually.
The connection to conversion rate optimization is direct. CLS is a conversion killer because it breaks user trust and causes accidental clicks. Fixing CLS is part of a complete conversion optimization strategy.
User Experience Impact: The Relatable Side
Beyond metrics and revenue, CLS impacts your users in frustrating ways:
- Reading interrupted mid-sentence: You're reading an article, get interested, then the page jumps and you lose your place
- Forms that jump while typing: You're filling out a contact form and the content shifts—did your text go in the right field?
- Buttons that move right before clicking: You target a button, the page shifts, and you accidentally click the button next to it
- Mobile frustration amplified: On smaller screens with touch precision requirements, a small shift causes you to tap the wrong target
Mobile User Impact
These aren't theoretical frustrations—they're daily experiences for millions of users on poorly optimized sites. Mobile users especially suffer because touch requires precision that mouse cursors don't need, and layout shifts turn what should be a simple click into a missed target.
Common Causes of CLS: Where Layout Shifts Come From
Understanding what causes layout shifts helps you identify and fix them in your own site. Most CLS issues fall into six major categories, listed here by frequency and impact.
The Six Major CLS Culprits
- Images Without Dimensions (60% of CLS Issues)
- Web Fonts Causing Text Reflow (25% of CLS Issues)
- Ads, Embeds, and Iframes
- Dynamically Injected Content
- Animations Using Layout Properties
- Late-Loading Third-Party Scripts
1. Images Without Dimensions (60% of CLS Issues)
This is the single biggest culprit. Images cause approximately 60% of CLS issues across the web. The reason is simple: browsers don't know how much space to reserve for an image until they download and analyze it.
Critical Issue
Why it happens:
When you add an image to a page without specifying width and height attributes, here's what occurs:
- Browser receives HTML and begins calculating layout
- No dimensions specified, so browser reserves no space for the image
- Browser continues laying out other content below where the image will go
- Image begins downloading
- Image finishes downloading and renders
- Browser now knows the image's size and must reflow all content below it
- Content shifts downward to make room
This is a guaranteed layout shift. Every image without pre-specified dimensions will cause this shift when it loads.
Most common scenarios:
- CMS-inserted images: Content management systems often insert images without width/height attributes in the HTML
- Responsive images with CSS only: Designers sometimes set image dimensions only in CSS (like
max-width: 100%), leaving HTML attributes unspecified - Dynamically loaded images: Content loaded via JavaScript from APIs often comes without dimension information
- User-uploaded content: Forums, comment sections, and user-generated content areas frequently have images without dimensions
The fix is straightforward but requires systematic implementation across your entire site.
2. Web Fonts Causing Text Reflow (25% of CLS Issues)
The second biggest culprit is web fonts—custom typefaces you load from external sources (Google Fonts, Adobe Fonts, etc.). Web fonts cause approximately 25% of CLS issues.
Font Loading Process
Why it happens:
When you specify a custom web font in your CSS, the browser doesn't know how that font will render until it downloads the font file. Here's the sequence:
- Browser receives CSS with
@font-facedeclaration - Browser starts rendering text using a fallback system font (like Arial)
- Font file begins downloading
- Once font file arrives, browser swaps the fallback font for the custom font
- Custom font renders differently than the fallback (different dimensions, kerning, line height)
- Text reflows to accommodate the new font metrics
- Content below the text shifts to make room
This phenomenon is called FOUT (Flash Of Unstyled Text) or FOIT (Flash Of Invisible Text), depending on how the browser handles the transition.
Most common scenarios:
- Google Fonts and similar services: Loading fonts from external CDNs means the font file takes time to arrive
- Large font files: Unoptimized font files download slowly, extending the period where fallback fonts are visible
- Unmatched fallback fonts: Using Arial as your fallback when your custom font is a serif creates a dramatic visual shift
- Multiple font variants: Loading different weights and styles multiplies the problem
Advanced optimization techniques can reduce or nearly eliminate font-related CLS—we'll cover those in the fixes section.
3. Ads, Embeds, and Iframes
Ads and embedded content (YouTube videos, social media embeds, third-party widgets) create CLS challenges because their dimensions aren't always known in advance.
Third-Party Content Challenges
Why it happens:
Ad networks work differently than your own images. When you embed a Google AdSense ad, you typically don't know exactly what ad will be served or how much space it will need. YouTube embeds have standard dimensions, but social media embeds vary. Third-party widgets like chat tools, review systems, and recommendation engines load asynchronously and can have unpredictable heights.
The sequence for an ad:
- You reserve some space for an ad
- Ad network code loads
- Ad renders (might be different size than your container)
- If the ad is larger than expected, content shifts
- If the ad never loads, you have empty space
Most common scenarios:
- Google AdSense auto ads: These insert ads automatically throughout your content without you controlling exact placement or size
- Social media embeds: Twitter/X embeds, Instagram embeds, and similar typically load their content late
- Third-party widgets: Chat tools (Intercom, Drift), review systems (Trustpilot, Yotpo), and recommendation engines
Ads are particularly challenging because optimizing for CLS might reduce your ad revenue (by limiting ad sizes). We'll discuss balancing this trade-off.
4. Dynamically Injected Content
Modern web applications often load content via JavaScript after the initial page render. This is different from server-rendered content and creates unique CLS challenges.
Dynamic Content Timing
Why it happens:
When JavaScript modifies the DOM (the underlying code that renders the page), it can add content in unexpected ways:
- Initial page renders with only above-fold content
- User sees stable page
- JavaScript runs and injects content (a notification banner, a load-more section that appends content, etc.)
- New content pushes existing content around
- Shift occurs and counts toward CLS
Most common scenarios:
- "Load More" buttons: Clicking the button shows new content that pushes existing content down
- Pop-ups and alerts: Notification banners, cookie consent, or alert dialogs inserted after page load
- GDPR/cookie notices: Many sites inject compliance banners that push content down
- Lazy-loaded content above the fold: Content that should have loaded initially but instead loads via JavaScript
The key distinction: shifts within 500ms of user interaction don't count toward CLS. So a shift that happens when you click "Load More" is fine. A shift that happens on its own (auto-playing video loading, automatic notification popping up) counts against you.
5. Animations Using Layout Properties
Some developers accidentally create CLS issues by animating the wrong CSS properties. Not all animations cause shifts, but certain ones do.
Animation Performance Impact
Why it happens:
CSS properties fall into two categories: properties that trigger layout recalculation (like height, width, top, left), and properties that don't (like transform, opacity). Animating layout properties forces the browser to recalculate the layout on every frame of the animation.
Most common scenarios:
- Accordion/dropdown animations: Height transitions that expand content
- Slide-in panels: Position or margin transitions that move elements
- Hover effects: Height or width changes on hover that reflow surrounding content
- Smooth transitions: Animating any dimension property
These animations don't just create CLS in the immediate element—they affect everything below it that must shift to accommodate the change.
6. Late-Loading Third-Party Scripts
Similar to dynamically injected content, third-party JavaScript can cause CLS when it modifies the page after initial render.
Why it happens:
Analytics scripts, chat widgets, review plugins, and other third-party tools often inject UI elements (like a floating chat button or review counter) after the page loads.
Most common scenarios:
- Chat widgets: Floating chat buttons that appear after page load
- Analytics (Google Analytics, Facebook Pixel): These usually don't cause CLS but can trigger other scripts that do
- Review widgets: Trustpilot, Yotpo, and similar inject review displays
- Social plugins: Facebook like buttons, Twitter share buttons
How to Measure CLS: Three Approaches
To fix CLS, you first need to measure it accurately. There are three measurement approaches, each serving different purposes: field data (real users), lab data (testing), and detailed debugging.
Field data measures CLS from actual visitors to your site. This is the most accurate representation of user experience because it captures real-world network conditions, device types, and user behavior.
Google Search Console
Google Search Console's Core Web Vitals report shows CLS data for your actual visitors. Navigate to Experience > Core Web Vitals to see:
- CLS scores for mobile and desktop separately
- Data aggregated over a 28-day period
- Which pages have issues
- Good vs. poor performance breakdown
GSC shows data at the 75th percentile, matching Google's ranking criteria. This is your most authoritative source for whether your site passes or fails Core Web Vitals.
To access:
- Open Google Search Console
- Select your property
- Go to Experience section
- Click Core Web Vitals
- View CLS data for mobile and desktop
The downside: GSC updates are slow (28-day rolling window) and doesn't show real-time impact of recent changes. But it's Google's official measurement, so it matters most for rankings.
Chrome User Experience Report (CrUX)
The Chrome User Experience Report provides real user data from millions of Chrome browsers, available through:
- PageSpeed Insights (easiest)
- Google BigQuery (for detailed analysis)
- CrUX API (for programmatic access)
CrUX data represents actual Chrome user experiences, making it highly reliable. It shows 75th percentile scoring matching Google's ranking criteria.
To access:
- Go to PageSpeed Insights
- Enter your URL
- Check "Show existing visitor data" section
- View CrUX CLS metrics for mobile and desktop
CrUX updates weekly, providing faster feedback than GSC. If you have sufficient traffic, this is your best real-time measurement.
For analytics dashboards and custom monitoring, we can set up continuous CrUX tracking that alerts you to CLS regressions.
Lab data comes from controlled testing environments where you simulate how your page loads. Lab tests are deterministic and useful for debugging but don't perfectly match real-world conditions.
PageSpeed Insights
PageSpeed Insights combines lab data (Lighthouse testing on simulated conditions) with field data (CrUX). This makes it perhaps the most useful single tool:
- Shows both lab and field CLS scores
- Lab score shows how your page performs under standard conditions
- Identifies which elements are causing shifts
- Available instantly for any URL
To use:
- Go to pagespeed.web.dev
- Enter your URL
- Click Analyze
- Review CLS metric and diagnostics
- Check recommendations
PageSpeed Insights is perfect for quick debugging and verifying fixes before deploying to production. However, the lab score might differ from your real-world field data due to differences in network speed, device type, and user behavior.
Chrome DevTools
Chrome DevTools provides the most detailed CLS debugging available. The Performance panel records your page load and shows:
- Exact timing of layout shifts
- Which elements shifted
- Before/after visual comparison
- Correlation with resource loading (images, fonts, etc.)
To use Chrome DevTools:
- Open Chrome DevTools (F12 or right-click > Inspect)
- Go to Performance tab
- Click Record
- Reload the page or interact with it
- Stop recording
- Look for "Layout Shifts" track in timeline
The purple bars in the Layout Shifts track represent CLS events. Click on each one to see details about what shifted and why.
Lighthouse
Lighthouse runs automated accessibility and performance audits, available through:
- Chrome DevTools (right-click > Inspect > Lighthouse tab)
- npm (
npm install -g @google/lighthouse) - CI/CD pipelines for automated testing
Lighthouse reports CLS and provides diagnostics. Running Lighthouse in your development workflow catches CLS regressions before deployment.
WebPageTest
For advanced analysis, WebPageTest offers:
- Testing from multiple geographic locations
- Multiple device types and connection speeds
- Filmstrip view showing visual progression
- Detailed waterfall analysis correlating resource loading with layout shifts
WebPageTest is particularly useful for identifying which third-party resources cause shifts.
Let's walk through the most effective debugging process using Chrome DevTools, the most detailed tool available.
Debugging CLS with Chrome DevTools: Step-by-Step
Let's walk through the most effective debugging process using Chrome DevTools, the most detailed tool available.
Enable Layout Shift Regions
First, enable the visual indicator that shows layout shifts in real-time:
Step-by-Step Setup
- Open Chrome DevTools: Press F12 (Windows/Linux) or Cmd+Option+I (Mac)
- Open the Command Menu: Press Cmd+Shift+P (Mac) or Ctrl+Shift+P (Windows/Linux)
- Type "Show Rendering": The command will appear
- Press Enter: This opens the Rendering panel
- Find "Layout Shift Regions": Scroll down in the Rendering panel
- Check the box: Enable "Layout Shift Regions"
- Reload your page: Purple flashes will appear where layout shifts occur
Every purple rectangle indicates an unexpected layout shift. This visual feedback is invaluable for understanding what's shifting on your page.
Use the Performance Panel
For detailed analysis of when and why shifts occur:
Performance Panel Analysis
- Open Performance panel: Go to Chrome DevTools > Performance tab
- Click Record: Start recording (or reload page while recording)
- Interact with page (scroll, click, etc.) if testing specific interactions
- Stop Recording: Click the stop button after 5-10 seconds
- Look for "Layout Shifts" track: Scroll down in the timeline
- Purple bars = shifts: Each purple bar represents a CLS event
- Click a purple bar: View details about what element shifted
The Performance panel shows:
- When shifts occur in relation to other events (image loading, script execution)
- Which element shifted
- How much it shifted (impact and distance fractions)
- Visual before/after comparison
This temporal correlation is critical. If a shift happens exactly when an image downloads, images without dimensions are likely the culprit.
Identify the Root Cause
Once you've identified when a shift occurs, ask these diagnostic questions:
Root Cause Analysis Questions
What element shifted?
- Is it text content (likely font-related)
- Is it an image (likely image dimensions)
- Is it a container (likely dynamic content)
What loaded just before the shift?
- Did an image start loading? (Images without dimensions)
- Did a font swap in? (Font loading issue)
- Did a script run? (Dynamic content)
- Did an ad load? (Ad containers)
Is it consistent or intermittent?
- Every page load? (Systematic issue like missing image dimensions)
- Sometimes? (Timing-dependent, like slow font loading)
- Only on certain devices/networks? (Mobile-specific or network-dependent)
Pattern matching:
- Shift correlates with image download = Missing width/height attributes
- Shift correlates with font loading = Font fallback mismatch or preloading issue
- Shift happens after DOM mutation = JavaScript injecting content without reserving space
- Shift happens when ad loads = Ad container without reserved dimensions
- Shift happens during animation = Animation using layout properties instead of transforms
How to Fix CLS: Complete Solutions
Now for the practical part—actually fixing layout shifts. Each solution addresses specific causes, with implementation examples for different scenarios.
Fix 1: Set Image Dimensions (Most Important)
Since images cause 60% of CLS issues, this is the highest-impact fix. The solution is simple but must be applied systematically across your entire site.
Basic Implementation: HTML Attributes
The simplest and most effective approach is adding width and height attributes to your HTML:
<img
src="hero.jpg"
width="1200"
height="630"
alt="Hero image"
>
Why this works: Modern browsers calculate the aspect ratio from width and height attributes. Even though the actual rendered size might be different (CSS can shrink it), the browser reserves the correct proportional space immediately. When the image loads, it fills the pre-reserved space without causing layout shifts.
The browser calculates: aspect-ratio = width / height = 1200/630 = 1.90. This aspect ratio is applied to reserve space, regardless of how CSS modifies the actual display size.
Important: The width and height values should match the actual image file dimensions. If your image is 1200×630 pixels, use those numbers. Don't guess or use different numbers just for layout.
Responsive Images: HTML + CSS Combined
For images that need to scale with the viewport, combine HTML dimensions with responsive CSS:
<img
src="hero.jpg"
width="1200"
height="630"
alt="Hero image"
style="max-width: 100%; height: auto;"
>
Why this works:
- HTML attributes establish the aspect ratio
- CSS
max-width: 100%makes the image responsive to its container height: autoscales height proportionally- Browser reserves space immediately based on the aspect ratio, then CSS makes it responsive
This is the recommended approach for most images. The HTML dimensions establish the aspect ratio for space reservation, while CSS handles responsive behavior.
Modern CSS: aspect-ratio Property
For images where you don't know exact dimensions (like dynamically-sized images from APIs), use CSS aspect-ratio:
.image-container {
aspect-ratio: 16 / 9;
width: 100%;
}
.image-container img {
width: 100%;
height: 100%;
object-fit: cover;
}
Why this works:
- Container reserves space based on the aspect ratio before image loads
- Image fills the container using
object-fit: cover - Even though you don't know the exact pixel dimensions, the aspect ratio ensures proper space reservation
- Works for responsive images across all viewport sizes
Use this when you can't set specific width/height but know the aspect ratio (16:9, 4:3, 1:1, etc.).
Next.js Image Component (Recommended)
If you're building with Next.js, use the built-in Image component:
import Image from 'next/image'
<Image
src="/hero.jpg"
width={1200}
height={630}
alt="Hero image"
priority // for above-fold images
/>
Why this works:
- Automatic dimension calculation and aspect ratio
- Lazy loading built-in for below-fold images
- Optimized delivery (automatic WebP/AVIF conversion)
- Responsive image srcset generation
- CLS prevention built-in
The priority prop should be used for above-fold images (this disables lazy loading and prioritizes loading). For below-fold images, remove priority and let Next.js lazy load automatically.
For Next.js web development services, we build CLS prevention directly into the architecture.
Fix 2: Optimize Web Fonts
Font-related CLS is more complex but entirely fixable with modern techniques. We'll cover approaches from basic to advanced.
Font Loading Strategies
Preload Critical Fonts
The fastest solution is telling the browser to load critical fonts as early as possible:
<head>
<link
rel="preload"
href="/fonts/inter-var.woff2"
as="font"
type="font/woff2"
crossorigin
>
</head>
Why it works:
- Font starts downloading immediately, before CSS is parsed
- Font is available when text renders
- Reduces or eliminates the "flash" period where fallback fonts display
- Fonts available for initial paint = minimal or zero shift
Important: Only preload fonts actually used above the fold. Preloading unnecessary fonts wastes bandwidth. For most sites, preload one or two font weights of your primary typeface.
Use font-display: swap (Basic Font Control)
The font-display CSS descriptor controls how browsers handle font loading. The swap value is usually best:
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2');
font-display: swap;
}
font-display options:
- swap: Show fallback immediately, swap to custom font when it arrives. Best for most cases.
- block: Hide text until custom font loads (creates a "flash of invisible text"). Not recommended.
- optional: Use fallback if custom font is slow; don't swap if already rendered. Good for non-critical fonts.
- fallback: Show fallback immediately, try to swap but abandon attempt quickly
- auto: Browser decides (usually equivalent to block)
With font-display: swap, text renders immediately in the fallback font, then swaps to the custom font when it arrives. This is visible to users but avoids invisible text.
Limitation: swap can still cause layout shift if the fallback font has significantly different dimensions than the custom font.
Advanced: Match Fallback Fonts with size-adjust
This is cutting-edge optimization that nearly eliminates font-related CLS. The size-adjust descriptor (and related properties) let you adjust fallback font dimensions to match your custom font:
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2');
font-display: swap;
}
/* Fallback font adjusted to match Inter dimensions */
@font-face {
font-family: 'Inter Fallback';
src: local('Arial');
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
body {
font-family: 'Inter', 'Inter Fallback', sans-serif;
}
How it works:
- Create a fallback font face with size adjustments
size-adjustscales the fallback font to match custom font heightascent-override,descent-override, andline-gap-overridefine-tune metrics- When custom font arrives, swap is nearly imperceptible because dimensions match
Impact: Can reduce font-related CLS by up to 70%, sometimes nearly to zero.
How to calculate values: This requires comparing font metrics between your custom font and fallback. Tools automate this:
- Fontaine: npm package that automatically calculates these values
- Capsized: Online tool for calculating font adjustments
- Manual approach: Use FontForge or similar tools to inspect font metrics
For simpler implementation, use Fontaine:
npm install @unjs/fontaine
Then configure it in your build process to automatically add size-adjust values to your @font-face rules.
Self-Host Fonts (Recommended for Performance)
Move your fonts from external CDNs to your own server:
Why it helps:
- Fonts load from same domain = faster loading (no extra DNS lookup)
- Better caching control
- No dependency on third-party service uptime
- Full preload control
How to implement:
- Download font files: Google Fonts and other foundries offer downloadable packages
- Host on your server: Place in
/public/fonts/or similar - Update @font-face: Point to local URLs instead of external CDN
- Preload critical variants: Use preload link tags for above-fold fonts
- Implement WOFF2 with fallbacks: Modern browsers support WOFF2; older browsers fall back to WOFF
Example:
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2-variations');
font-display: swap;
}
Self-hosting is particularly important if you're using many font weights or variants.
Fix 3: Reserve Space for Ads and Embeds
Ads and embedded content create CLS challenges because their dimensions might not be predictable. The solution is reserving space before they load.
Space Reservation Strategies
For standard ad sizes, reserve space with CSS:
.ad-container {
min-height: 250px; /* Standard ad height */
aspect-ratio: 300 / 250; /* Standard ad size (300×250) */
background: #f5f5f5; /* Light gray placeholder */
}
Why it works:
- Space is reserved before ad loads
- If ad is smaller, placeholder fills the space
- If ad is larger, container expands to fit
- Visual feedback to users that ad is loading
Important nuance: Use min-height rather than exact height. Some ad networks serve different sizes, and using exact height might cut off ads or create empty space.
Common ad sizes:
- 300×250 (Medium Rectangle)
- 728×90 (Leaderboard)
- 320×50 (Mobile Banner)
- 300×600 (Half Page)
Reserve space based on the sizes your ad network actually serves.
Responsive Ads (More Complex)
For responsive ad containers that scale with viewport, use the padding-bottom technique:
.ad-container {
position: relative;
width: 100%;
padding-bottom: 83.33%; /* Aspect ratio: 6/5 */
}
.ad-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
Why it works:
- Padding-bottom (as percentage) maintains aspect ratio
- Iframe fills container at any viewport size
- No shift when ad loads because space is pre-allocated
Calculate padding-bottom: (height / width) × 100. For a 300×250 ad: (250/300) × 100 = 83.33%.
YouTube/Vimeo Embeds
Video embeds have standard aspect ratios, making them easy to optimize:
.video-container {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
}
.video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
HTML:
<div class="video-container">
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>
</div>
This works for all 16:9 videos. For different aspect ratios:
- 4:3 video:
padding-bottom: 75% - 1:1 square:
padding-bottom: 100% - 21:9 ultrawide:
padding-bottom: 42.86%
Third-Party Widgets (Reviews, Chat, etc.)
Widgets from services like Trustpilot, Yotpo, Intercom, and Drift create unpredictable CLS. Strategies include:
- Lazy load below the fold: Only load widgets when users scroll to them
- Reserve fixed space: If you know approximate dimensions, reserve that space
- Load on interaction: Load widget when user clicks "Show Reviews" or similar
- Server-side render: If widget supports it, render on server to include in initial HTML
For AI and automation services, we can implement detection systems that identify third-party script impact and optimize loading.
Fix 4: Handle Dynamic Content Properly
When JavaScript adds content after page load, reserve space or position intelligently to avoid shifts.
Dynamic Content Best Practices
Position these absolutely or fixed so they don't push content:
Good approach (no shift):
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
}
Bad approach (causes shift):
.cookie-banner {
position: relative; /* Pushes content up when added to DOM */
}
The difference: fixed takes the element out of document flow entirely (like a floating panel), while relative keeps it in the flow and affects surrounding content.
Cookie Banners and Notifications
Position these absolutely or fixed so they don't push content:
"Load More" Buttons
When users click "Load More" and new content appends, the shift happens within user interaction, so technically it doesn't count against CLS. However, good practice is still to show placeholders immediately:
loadMoreButton.addEventListener('click', async () => {
// Show skeleton/placeholders immediately
const skeletons = createSkeletonLoaders();
container.appendChild(skeletons);
// Fetch new content
const newContent = await fetchContent();
// Replace skeletons with actual content
skeletons.forEach((skeleton, index) => {
skeleton.replaceWith(newContent[index]);
});
});
Why it works:
- Placeholders reserve space and provide visual feedback
- Actual content replacement doesn't cause shift because space is pre-allocated
- User expects content to appear after clicking
AJAX-Loaded Content
For content loaded via AJAX without user interaction, reserve space before content arrives:
// Calculate approximate height needed
const estimatedHeight = 300; // or calculate based on content type
const placeholder = createPlaceholder(estimatedHeight);
container.appendChild(placeholder);
// Fetch content
const content = await fetchContent();
// Replace placeholder with actual content
placeholder.replaceWith(content);
This prevents shift by pre-allocating space. If your estimate is close, shift is minimal. If it's off, only the difference shifts.
Fix 5: Animate with Transforms, Not Layout Properties
Some CSS properties trigger layout recalculation on every animation frame, while others don't. Use the right ones.
Bad: Animating Layout Properties
Avoid these animations:
.dropdown {
height: 0;
transition: height 0.3s;
}
.dropdown.open {
height: 200px; /* Triggers layout recalculation every frame */
}
Why it's bad:
- Height changes force browser to recalculate layout
- Every animation frame recalculates (60 times per second)
- Surrounding content must shift to accommodate
- Counts toward CLS
- Expensive and causes jank (stuttering animation)
Good: Using Transforms
Use transforms instead:
.dropdown {
transform: scaleY(0);
transform-origin: top;
transition: transform 0.3s;
}
.dropdown.open {
transform: scaleY(1); /* No layout recalculation */
}
Why it works:
- Transforms are composited (GPU-accelerated)
- No layout recalculation needed
- Doesn't affect surrounding elements
- Smooth, 60fps animations
- Doesn't count toward CLS
Transform cheat sheet - Replace these with transforms:
| What you want | Avoid | Use instead |
|---|---|---|
| Resize element | height / width | transform: scale() |
| Move element | top / left / right / bottom | transform: translate() |
| Rotate element | transform: rotate() (this is fine!) | transform: rotate() |
| Change margins/padding (if animating) | margin / padding | transform: translate() |
The will-change Property
Advanced optimization: hint to the browser that an element will animate:
.element-that-will-animate {
will-change: transform;
}
.element-that-will-animate.animating {
transition: transform 0.3s;
transform: translateX(100px);
}
Why it helps:
- Tells browser to pre-create composite layers
- Prevents jank on animation start
- Optimizes GPU usage
Important caveat: Overusing will-change actually hurts performance. Only apply to elements that actually animate, remove it when animation is complete, and don't exceed 5-10 elements with will-change simultaneously.
Fix 6: Lazy Load Below the Fold Only
Lazy loading can prevent CLS—if done correctly. The critical rule: never lazy load above-the-fold images.
Native Lazy Loading
The simplest approach using the loading attribute:
<img
src="below-fold.jpg"
width="800"
height="600"
loading="lazy"
alt="Below fold image"
>
Why it works:
- Browser handles lazy loading automatically
- Dimensions are still set, so space is reserved
- Image loads only when user scrolls near it
- No CLS when image finally loads because space was reserved
Critical Rule
Never apply to above-fold images:
<!-- DON'T DO THIS -->
<img
src="hero.jpg"
loading="lazy" <!-- Wrong! -->
alt="Hero"
>
Above-fold lazy loading defeats its own purpose:
- Hero image won't start loading until HTML is parsed
- Image renders late, causing CLS
- Delays LCP (Largest Contentful Paint)
Intersection Observer (Custom Lazy Loading)
For more control, use JavaScript to detect when images enter the viewport:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // Load actual image
observer.unobserve(img);
}
});
});
// Only observe below-fold images
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
HTML:
<img
data-src="below-fold.jpg"
width="800"
height="600"
alt="Below fold image"
>
Why it works:
- Images load only as user scrolls near them
- Dimensions pre-allocated, so no CLS
- Improves page load speed
- Works across all browsers
This approach is more flexible than native lazy loading but requires more code.
CLS Optimization Checklist
Use this checklist to systematically address CLS across your entire site:
Complete CLS Optimization Checklist
Images
- All above-fold images have width and height attributes
- All below-fold images have width and height attributes
- Responsive images use HTML dimensions plus CSS max-width
- Above-fold images use
loading="eager"or no loading attribute - Below-fold images use
loading="lazy" - Image containers use
aspect-ratioCSS when exact dimensions unknown - Next.js/framework image components used where applicable
Fonts
- Critical fonts preloaded with
<link rel="preload"> - All @font-face rules include
font-display: swap - Fallback fonts matched using size-adjust (optional but recommended)
- Using self-hosted fonts instead of external CDN (recommended)
- Unused font weights and styles removed
- Font subsetting implemented for non-Latin scripts
Ads and Embeds
- Ad containers have
min-heightoraspect-ratio - Ad slots don't collapse if ad doesn't fill them
- YouTube/video embeds use aspect-ratio containers
- Third-party widgets lazy loaded below fold
- Social embeds have reserved space
Dynamic Content
- Cookie banners positioned
fixedorabsolute - AJAX-loaded content uses placeholders before loading
- "Load More" shows skeleton loaders immediately on click
- Pop-ups and overlays don't push main content
- Late-loading content positioned outside normal flow
Animations
- Animations use
transforminstead of layout properties -
will-changeapplied sparingly (only on actively animating elements) - Accordion/dropdown animations don't cause shifts
- Hover effects don't trigger layout changes
Testing
- Tested in Chrome DevTools with Layout Shift Regions enabled
- Lighthouse CLS score < 0.1
- PageSpeed Insights shows passing CLS (both lab and field)
- Google Search Console shows no CLS issues
- Tested on real mobile devices (not just desktop DevTools)
Advanced CLS Optimization Techniques
For those wanting to optimize beyond the basics, these advanced techniques provide additional improvements.
Advanced CLS Techniques
Optimize Font Loading with Subsetting
Create font subsets containing only characters you actually use. This reduces file size and speeds loading:
Tools:
- Glyphhanger: CLI tool that analyzes your HTML and creates subsets
- Font Subsetter: Web-based tool for creating subsets
- Google Fonts with
unicode-range: Automatically serves subsets
Example with unicode-range:
/* Latin characters only (most common) */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-latin.woff2') format('woff2');
unicode-range: U+0000-00FF;
}
/* Greek and Coptic characters */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-greek.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
The browser downloads only the subset needed for your page's language. Non-Latin scripts can be huge, so subsetting them dramatically improves font loading speed and reduces font-related CLS.
Preload LCP Images
If your hero image is the Largest Contentful Paint element (the biggest visible image or text block when page loads), preload it aggressively:
<link
rel="preload"
as="image"
href="/hero.jpg"
imagesrcset="/hero-320w.jpg 320w, /hero-640w.jpg 640w, /hero-1280w.jpg 1280w"
imagesizes="100vw"
>
Why it helps:
- Image starts downloading immediately (not waiting for CSS)
- Improves both LCP metric and reduces CLS risk
- Critical for performance-sensitive sites
This is an advanced optimization that helps beyond just CLS—it also speeds up your LCP metric.
For complete LCP optimization, preloading is often the single most impactful change.
Content Visibility API
For long pages with lots of off-screen content, use CSS containment to skip rendering content until needed:
.section {
content-visibility: auto;
contain-intrinsic-size: 0 500px;
}
Why it works:
- Browser skips rendering off-screen sections
contain-intrinsic-sizereserves approximate space- Initial render is much faster
- Content renders as user scrolls near it
- Fewer late-rendering elements = less CLS
Browser support: Modern browsers (Chrome, Edge, Opera) support this. Firefox support is in development.
Server-Side Rendering (SSR) for Dynamic Content
If your site heavily uses client-side rendering, migrating to server-side rendering eliminates CLS from late-rendering content:
How it helps:
- Content renders on server and arrives in initial HTML
- Browser renders complete page on first load
- No JavaScript-injected content appearing late
- Perfect CLS opportunity if your content is deterministic
Frameworks supporting SSR:
- Next.js (built-in)
- Nuxt.js (Vue)
- SvelteKit
- Remix
- Astro
Server-side rendering is a more fundamental architectural choice than individual CLS fixes, but the CLS benefits are substantial.
For Next.js web development services, we build with SSR by default, eliminating many CLS issues at the architecture level.
Resource Hints for Third-Party Content
Optimize how quickly third-party resources load using resource hints:
<!-- DNS prefetch for ad networks (resolves domain early) -->
<link rel="dns-prefetch" href="//pagead2.googlesyndication.com">
<!-- Preconnect to critical third-party (establishes full connection) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Prefetch non-critical resources -->
<link rel="prefetch" href="//analytics.google.com/analytics.js">
Why it helps:
- DNS prefetch reduces domain lookup time
- Preconnect establishes TCP connection early
- Resources arrive faster = less time in unstable state
- Fewer late shifts from third-party content
Use preconnect sparingly (3-4 max) as it's expensive. Use dns-prefetch for resources you're not immediately loading.
Common CLS Mistakes to Avoid
Learning from others' mistakes accelerates your own optimization.
Common CLS Mistakes to Avoid
Mistake 1: Lazy Loading Everything
The problem: Some developers lazy load all images, including above-fold ones, thinking it improves performance. It does the opposite for CLS.
Lazy loading above-fold images:
- Delays image download until HTML is fully parsed
- Image loads much later than it otherwise would
- Causes CLS when late image renders
- Slows down LCP metric
Solution: Only lazy load below-fold images. Use loading="eager" or omit the loading attribute entirely for hero images.
<!-- Good: eager loading for above-fold -->
<img src="hero.jpg" loading="eager" width="1200" height="630" alt="Hero">
<!-- Good: lazy loading for below-fold -->
<img src="thumbnail.jpg" loading="lazy" width="400" height="300" alt="Thumbnail">
Mistake 2: Not Testing on Real Devices
The problem: Desktop DevTools don't show all mobile-specific CLS issues. Font rendering differs on mobile, touch interactions behave differently, and network conditions are worse.
Testing only in desktop DevTools is misleading because:
- Mobile has slower networks (fonts take longer to load)
- Mobile has smaller viewports (shifts appear larger)
- DevTools can't simulate all real-world conditions
Solution: Test on actual mobile devices whenever possible. Use Chrome's remote debugging for detailed analysis on real phones.
Mistake 3: Fixing Lab Data, Ignoring Field Data
The problem: Your lab scores (Lighthouse, PageSpeed Insights) show perfect CLS while GSC shows issues. Real users experience shifts that synthetic tests don't catch.
Why it happens:
- Lab data uses standard conditions (specific network speed, device)
- Field data includes all users (various networks, devices, interactions)
- Lab tests run once; field data aggregates thousands of visits
- Third-party scripts behave differently in lab vs. real-world
Solution: Prioritize field data (GSC Core Web Vitals, CrUX). Lab data is for debugging specific issues, not final measurement.
Mistake 4: Setting Incorrect Image Dimensions
The problem: Width/height attributes don't match the actual image aspect ratio, causing images to stretch or shift when proper dimensions are applied via CSS.
<!-- Image is 1200×800, but you set wrong dimensions -->
<img
src="photo.jpg"
width="1000" <!-- Wrong -->
height="1000" <!-- Wrong -->
style="max-width: 100%; height: auto;"
alt="Photo"
>
Result: Browser reserves 1:1 square space, image loads at 3:2 ratio, content shifts.
Solution: Use actual image dimensions in HTML attributes.
<!-- Correct -->
<img
src="photo.jpg"
width="1200" <!-- Actual image width -->
height="800" <!-- Actual image height -->
style="max-width: 100%; height: auto;"
alt="Photo"
>
Check image properties (right-click > Properties on Windows, Get Info on Mac) to confirm actual dimensions.
Mistake 5: Overusing will-change
The problem: Applying will-change to too many elements consumes memory and degrades performance.
/* DON'T DO THIS */
.animate-left { will-change: left; }
.animate-right { will-change: right; }
.animate-scale { will-change: transform; }
.animate-opacity { will-change: opacity; }
/* ...30 more elements with will-change... */
Each will-change forces browser to create a composite layer, consuming GPU memory.
Solution: Apply will-change only to elements that actively animate, and remove it after animation completes.
// Add will-change before animation
element.style.willChange = 'transform';
// Animate
element.style.transform = 'translateX(100px)';
// Remove after animation ends
setTimeout(() => {
element.style.willChange = 'auto';
}, 300); // After transition duration
Max 5-10 elements with will-change simultaneously.
Mistake 6: Forgetting About Fonts on Repeat Visits
The problem: Font swaps happen on first visit when the font isn't cached. On repeat visits, the cached font loads instantly. You test on your own computer (repeat visitor, cached font), see no shift, and assume the problem is solved. New visitors still experience shifts.
Solution: Test in incognito/private mode (empty cache) or use PageSpeed Insights (which tests uncached). Monitor field data (GSC) for all users, not just your own visits.
CLS Optimization by Platform
Different platforms have different CLS challenges. Platform-specific solutions:
WordPress
Common issues:
- Plugins inject content without reserved space
- Themes don't set image dimensions properly
- Page builders generate suboptimal markup
- Unoptimized fonts from external sources
Solutions:
- Use performance plugins (WP Rocket, Perfmatters)
- Ensure theme outputs image dimensions
- Lazy load only below-fold images
- Minimize page builder usage (hand-code critical sections)
- Self-host Google Fonts
Note: Digital Thrive builds custom web development instead of relying on WordPress. Custom implementations avoid these plugin and theme-related CLS issues entirely.
Shopify
Common issues:
- Theme not optimized for Core Web Vitals
- Product images without dimensions
- Review widgets loading late
- Announcement bars pushing content
Solutions:
- Choose a Core Web Vitals-optimized theme
- Set product image aspect ratios
- Reserve space for review widgets
- Position announcement bars fixed/absolute
For ecommerce optimization, Shopify presents constraints that custom eCommerce design solutions don't have.
Next.js
Built-in solutions:
next/imagecomponent prevents CLS automaticallynext/fontoptimizes font loading- Automatic image optimization
Best practices:
import Image from 'next/image'
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export default function Page() {
return (
<main className={inter.className}>
<Image
src="/hero.jpg"
width={1200}
height={630}
alt="Hero"
priority // Disables lazy loading for above-fold
/>
</main>
)
}
Next.js handles most CLS issues automatically if you use the right components. For Next.js development, CLS prevention is built-in.
React (Client-Side Rendering)
Challenges:
- Client-side rendering = late content
- JavaScript dependencies
- Hydration shifts
Solutions:
- Reserve space with skeleton loaders
- Use suspense boundaries
- Consider SSR/SSG (Next.js, Gatsby, Astro)
- Set explicit dimensions in JSX
// Reserve space before content loads
<div style={{ width: '100%', aspectRatio: '16/9' }}>
{isLoading ? <SkeletonLoader /> : <Image src={src} />}
</div>
Implementation Roadmap: Prioritize Your Fixes
Not all CLS fixes have equal impact or effort. This roadmap helps you prioritize.
Phase 1: Quick Wins (Day 1)
Highest Impact, Lowest Effort
These fixes alone often improve CLS significantly:
1. Add image dimensions to all above-fold images
- Impact: Very High (images cause 60% of CLS)
- Effort: Low (HTML attribute changes)
- Time: 1-2 hours for typical site
2. Preload critical fonts
- Impact: Medium-High
- Effort: Low (add preload tags)
- Time: 30 minutes
3. Set min-height on ad containers
- Impact: High (if you use ads)
- Effort: Low (CSS changes)
- Time: 1 hour
Expected improvement: Significant CLS reduction
Phase 2: Systematic Fixes (Week 1)
Medium impact, moderate effort. These improve CLS substantially:
1. Add dimensions to ALL images (above and below fold)
- Impact: High
- Effort: Medium (full site audit required)
- Time: 4-8 hours depending on site size
2. Implement font-display: swap
- Impact: Medium
- Effort: Low-Medium
- Time: 1-2 hours
3. Fix embed aspect ratios (YouTube, Twitter, etc.)
- Impact: Medium
- Effort: Low-Medium
- Time: 2-3 hours
4. Optimize lazy loading (below-fold only)
- Impact: Medium
- Effort: Medium
- Time: 2-4 hours
Expected improvement: Substantial CLS reduction from baseline
Phase 3: Advanced Optimization (Ongoing)
Lower impact individually but compounding:
1. Implement size-adjust for fonts
- Impact: Low-Medium
- Effort: High (requires metric calculation)
- Time: 4-6 hours
2. Migrate animations to transforms
- Impact: Low-Medium
- Effort: Medium-High (code changes)
- Time: Varies by complexity
3. Self-host fonts
- Impact: Low-Medium
- Effort: Medium
- Time: 2-3 hours
4. Add placeholders for dynamic content
- Impact: Low-Medium
- Effort: Medium-High
- Time: Varies by content types
Expected improvement: Near-optimal CLS scores
Priority Matrix: What to Fix First
CLS Fix Priority Matrix
| Issue | Impact | Effort | Priority |
|---|---|---|---|
| Images without dimensions | Very High | Low | Critical |
| Fonts without preload | High | Low | Critical |
| Ads without reserved space | High | Low | Critical |
| Embeds without aspect ratio | Medium | Low | High |
| Late-loading widgets | Medium | Medium | High |
| Font fallback optimization (size-adjust) | Medium | High | Medium |
| Animation with layout properties | Low-Medium | Medium | Medium |
Use this matrix to allocate development resources efficiently.
Validating Your CLS Fixes
Before and after deployment, validate that fixes actually work.
Pre-Deployment Testing
Before pushing changes live:
Pre-Deployment Testing Checklist
1. Chrome DevTools Performance Panel
- Open Performance tab
- Record fresh page load
- Check Layout Shifts track
- Verify score < 0.1
- Test both desktop and mobile viewports
- Clear cache (DevTools > Settings > Network > disable cache)
2. Lighthouse in DevTools
- Run full audit
- Check CLS score
- Review diagnostics section
- Test in incognito mode (clean cache)
3. Multiple Network Conditions
- Fast 3G simulation
- Slow 3G simulation
- Throttled connection
- Verify CLS doesn't increase on slow networks
Post-Deployment Monitoring
After changes go live (wait 7-28 days for data):
Post-Deployment Monitoring
1. Google Search Console (28-day aggregation)
- Experience > Core Web Vitals
- Verify mobile and desktop improvements
- Confirm no regressions on other pages
- Monitor over time
2. PageSpeed Insights (immediate feedback)
- Test key landing pages
- Check both lab and field CLS scores
- Compare before/after values
- Review diagnostic changes
3. Real User Monitoring (if implemented)
- Monitor CLS across all users
- Segment by device type (mobile vs. desktop)
- Track improvements over time
- Set up alerts for regressions
For analytics dashboards and monitoring, we can create real-time CLS dashboards that alert you to problems immediately.
Testing Checklist
Comprehensive Testing Checklist
- Chrome DevTools Performance panel: Layout Shifts analyzed
- Lighthouse CLS score: <0.1
- PageSpeed Insights: Passing CLS (lab and field)
- Tested on slow network (Slow 3G simulation)
- Tested on real mobile device (not just desktop)
- Verified above-fold images load immediately
- Verified fonts don't cause visible reflow
- Checked all major pages (not just homepage)
- Monitored GSC for 28 days post-deployment
- No regressions on other Core Web Vitals (LCP, FID/INP)
CLS and Other Core Web Vitals
CLS doesn't exist in isolation. Let's explore how it connects to the other Core Web Vitals.
CLS + LCP (Largest Contentful Paint)
Both measure user experience during page load. They're often interconnected:
LCP + CLS Connections
Connections:
- LCP element (usually hero image) is common CLS source
- Optimizing images helps both metrics simultaneously
- Preloading helps both LCP and CLS
Optimization overlap:
- Preload LCP image → improves LCP, prevents CLS shift
- Set image dimensions → prevents CLS, enables faster LCP measurement
- Optimize font loading → improves LCP (if text is LCP element), reduces CLS
For complete LCP optimization guidance, many of the same techniques help both metrics.
CLS + FID (First Input Delay)
Less direct connection, but both relate to user experience:
Connections:
- Heavy JavaScript can delay both rendering (CLS) and interaction (FID)
- Optimizing third-party scripts helps both
- Render-blocking resources affect both
Optimization overlap:
- Reduce JavaScript execution → improves FID, reduces late content shifts
- Defer non-critical scripts → improves FID, prevents late CLS
- Code splitting → improves FID, enables faster initial render
For FID optimization details, see the dedicated guide.
CLS + Page Speed
Faster loading = less time in unstable state:
Why page speed matters for CLS:
- Quick font/image loading = less shift opportunity
- Faster TTFB = earlier font loading = less shift window
- Faster image loading = less time between layout and image render
- Faster JavaScript = earlier dynamic content = fewer late shifts
Page speed optimization and CLS optimization complement each other.
Tools and Resources for CLS Optimization
Measurement Tools
Google Tools (Free, Official):
- PageSpeed Insights - Lab + field data combined
- Google Search Console - Real user CLS data (official ranking metric)
- Chrome DevTools - Detailed debugging
- Lighthouse - Automated auditing
Third-Party Tools (Continuous Monitoring):
- WebPageTest - Advanced waterfall analysis
- DebugBear - Continuous CLS monitoring
- SpeedCurve - Real user monitoring
- Calibre - Performance monitoring and alerts
Font Optimization Tools
- Fontaine - Automatic fallback font matching (generates size-adjust values)
- Capsized - Font metrics calculator
- Glyphhanger - Font subsetting for reducing file size
- Modern Font Stacks - System font recommendations
Image Optimization Tools
- Squoosh - Image compression and format conversion
- ImageOptim (Mac) - Batch image optimization
- Sharp - Node.js image processing
- Framework tools: Next.js Image, Nuxt Image components
Browser Extensions
- Web Vitals Chrome Extension - Real-time CWV monitoring
- Lighthouse - Quick audits in browser
How Digital Thrive Optimizes CLS
CLS optimization is part of Digital Thrive's comprehensive Technical SEO service, specifically within the User Experience (UX) dimension of our four-dimension audit framework.
Our Four-Dimension Technical SEO Framework
Digital Thrive's Technical SEO Framework
- Indexability - Can search engines access and crawl your content?
- Relevance - Does your site clearly signal what it's about?
- User Experience - Is your site fast, stable, and usable? ← CLS lives here
- Internal Authority - How is link equity distributed across pages?
CLS doesn't exist in isolation in our approach. Layout shift issues often correlate with other technical problems (render-blocking resources, poor image optimization, unoptimized fonts). We fix CLS as part of holistic site health, not as an isolated metric.
What You Get With Our Technical SEO Service
Complete Audit:
- Screaming Frog crawl identifying all images without dimensions
- Chrome DevTools analysis of layout shift sources
- GSC and CrUX data analysis for real-user CLS scores
- Prioritized list of CLS issues with impact ratings
Detailed Recommendations:
- Developer-ready specifications for each fix
- Code examples for your specific CMS or framework
- Before/after CLS score projections
- Impact vs. effort ratings for prioritization
Implementation Support:
- Guidance during development
- Testing procedures and validation methods
- Re-crawl and verification after deployment
- Ongoing monitoring setup
Why Work With Digital Thrive
The Digital Thrive Advantage
Holistic Understanding: We don't treat CLS as an isolated metric. We see how layout shifts connect to content strategy, conversion optimization, and broader technical architecture.
Real-World Prioritization: We focus on issues that actually impact your rankings and revenue, not vanity metrics. Our implementation roadmap is based on impact vs. effort.
Fast Delivery: Technical audits completed in days. Our AI-powered approach means faster analysis and recommendations.
Implementation-Ready: Not just "here's what's wrong." We provide exact specifications, code examples, and validation procedures.
For comprehensive SEO services that include technical optimization, we combine CLS fixes with content strategy, link authority, and keyword optimization for complete digital performance.
Frequently Asked Questions
What is a good CLS score?
Google defines 0.1 or less as good, measured at the 75th percentile. This means 75% of your page loads should have CLS ≤ 0.1.
Score ranges:
- 0.0 - 0.1: Good
- 0.1 - 0.25: Needs improvement
- Above 0.25: Poor
Aim for 0.1 or better to avoid ranking penalties and provide optimal user experience.
Does CLS affect SEO rankings?
Yes. CLS is one of three Core Web Vitals Google uses as a ranking factor (since June 2021). However, impact is small compared to content quality and relevance.
Practical impact:
- Sites with poor CLS might still rank if content is excellent
- Sites with excellent CLS but poor content will rank poorly
- When two sites have similar content, better CLS gives a ranking edge
- Mobile CLS matters more (mobile-first indexing)
Fixing CLS improves both rankings AND conversion rates, so it's worth the effort.
Can I have 0.0 CLS?
Technically yes, but rare. Zero CLS means absolutely zero layout shifts during entire page lifetime. Achieving this requires:
- All images with explicit dimensions
- All fonts preloaded and perfectly matched
- No dynamic content or ads
- No animations affecting layout
- No third-party scripts injecting content
Practical goal: Aim for <0.05 rather than perfect 0.0. The difference between 0.05 and 0.0 is imperceptible to users.
How long does it take to fix CLS issues?
Simple fixes (image dimensions):
- Small site (<50 pages): 1-2 hours
- Medium site (50-500 pages): 4-8 hours
- Large site (>500 pages): 1-2 days (can automate)
Complex fixes (fonts, ads, dynamic content):
- Font optimization: 2-4 hours
- Ad container fixes: 1-3 hours
- Dynamic content placeholders: 2 hours to several days
Total project:
- Audit: 1-2 days
- Implementation: 1-5 days depending on scope
- Validation: 7-28 days (waiting for field data)
Audits and recommendations typically deliver quickly. Implementation speed depends on your development resources.
Does lazy loading cause CLS?
Not if done correctly. Lazy loading can prevent CLS by reducing page weight and speeding initial load.
Done correctly:
- Set image dimensions even for lazy-loaded images
- Only lazy load below-fold content
- Use native
loading="lazy"attribute - Reserve space before image loads
Done incorrectly:
- Lazy load above-fold images (causes late rendering and CLS)
- No dimensions set (causes shift when loading)
- Lazy load everything (defeats purpose, slows LCP)
Rule: Lazy load below the fold with dimensions = zero CLS.
Can ads be optimized for CLS?
Yes, but with trade-offs. Ad dimensions aren't always predictable, but you can minimize shifts:
- Fixed ad slots: Reserve space with min-height or aspect-ratio
- Don't collapse empty slots: Keep space even if ad doesn't fill
- Static placements: Avoid auto-inserted ads that move content
- Lazy load ads: Load only when in viewport (reserve space first)
Trade-off: Some ad networks penalize you for limiting ad sizes. Balance CLS optimization with ad revenue.
Do fonts always cause CLS?
Not always. Whether fonts cause CLS depends on:
- Font loading speed: Faster fonts = less shift window
- Fallback font match: Better match = less visible shift
- font-display setting:
swapcan shift;optionalmight not - Preloading: Preloaded fonts available earlier = less shift
Modern techniques (size-adjust, preloading) can reduce or eliminate font-related CLS entirely.
Should I fix CLS on mobile or desktop first?
Mobile first. Google uses mobile-first indexing, so:
- Mobile CLS score affects rankings more
- Mobile users experience worse CLS (slower networks, smaller targets)
- Mobile shifts are more noticeable
- GSC reports mobile and desktop separately
Test and fix both, but prioritize mobile.
Can third-party scripts cause CLS?
Absolutely. Common culprits:
- Chat widgets (Intercom, Drift)
- Review widgets (Trustpilot, Yotpo)
- Social plugins (Facebook buttons, Twitter embeds)
- Ad scripts (Google AdSense)
- Analytics (usually minor impact)
Solutions:
- Lazy load below the fold
- Reserve space before script loads
- Load on interaction instead of on page load
- Evaluate necessity of each script
We audit third-party script impact as part of comprehensive technical SEO audits.
How do I test CLS on a staging site?
Use lab data tools since field data won't be available:
- Chrome DevTools - Most reliable for staging
- Lighthouse - Good for automated testing
- WebPageTest - Test from different locations/networks
Lab scores won't perfectly match production field data, but they're accurate for identifying issues. Deploy to production and monitor GSC for final validation.
Does CLS affect passing Core Web Vitals?
Yes. All three Core Web Vitals must pass at 75th percentile:
- CLS ≤ 0.1 (good)
- LCP ≤ 2.5s (good)
- FID ≤ 100ms (good) [FID being replaced by INP ≤ 200ms]
Failing any metric means failing overall. CLS alone can cause failure.
For complete Core Web Vitals optimization, all three metrics work together.
Next Steps
If You're Fixing CLS Yourself
Recommended workflow:
- Measure current CLS: Use GSC or PageSpeed Insights
- Identify sources: Chrome DevTools Performance panel
- Start with quick wins: Image dimensions, font preload, ad containers
- Test thoroughly: Multiple devices, network conditions, browsers
- Monitor field data: Wait 28 days, check GSC for improvements
Resources to bookmark:
- This guide (refer back as needed)
- web.dev CLS documentation
- Chrome DevTools Layout Shift Regions feature
- PageSpeed Insights for ongoing monitoring
If You Want Expert Help
Expert CLS Optimization Services
Digital Thrive's Technical SEO service includes comprehensive CLS optimization:
- Complete technical audit (CLS is part of UX dimension)
- Prioritized fix recommendations with impact ratings
- Developer-ready implementation specifications
- Validation testing and verification
- Ongoing monitoring and regression detection
What makes our approach different:
- Holistic (CLS + other technical factors)
- Fast delivery
- Implementation-focused (not just reporting)
- Real prioritization based on impact vs. effort
Learn more about Technical SEO services or get in touch with us to discuss your site's CLS issues.
We'll audit your site, identify what's causing layout shifts, and provide a clear implementation roadmap—typically within days of starting. Rather than generic recommendations, we provide specific, actionable solutions tailored to your site's architecture and business goals.
Sources
- Cumulative Layout Shift (CLS) | web.dev
- Optimize Cumulative Layout Shift | web.dev
- How to Fix Cumulative Layout Shift (CLS) in 2025 – Natclark
- What is Cumulative Layout Shift (CLS) And How to Fix It | NitroPack
- Cumulative Layout Shift (CLS) Optimization Guide 2025 | QuickFix WP
- How to Optimize Your Site for Cumulative Layout Shift in 2025 | Justuno
- Understanding Cumulative Layout Shift (CLS) | BrowserStack
- Understanding and Improving Cumulative Layout Shift (CLS) | SpeedCurve
- Fixing Layout Shifts Caused by Web Fonts | DebugBear
- Debug layout shifts | web.dev
- Layout Shift Culprits: Debug CLS On Your Website | DebugBear
- Debugging and Optimizing Cumulative Layout Shift (CLS) | ditdot
- How to create high-performance CSS animations | web.dev
- Preventing Layout Shifts: Mastering CSS Transitions and Animations | Pixel Free Studio
- aspect-ratio - CSS | MDN