What Is CLS and Why Does It Matter for Shopify?
Cumulative Layout Shift (CLS) is one of Google's three Core Web Vitals — the metrics that directly influence your search rankings and measure real user experience. While LCP measures how fast content loads and INP measures interactivity, CLS measures visual stability — whether elements on your page stay put or jump around unexpectedly.
Picture this: a customer is about to tap "Add to Cart" on your product page when suddenly a review widget loads above, pushing the button down. They accidentally tap a different link and leave your store. That's a CLS problem — and it costs you real revenue.
CLS is calculated by multiplying the impact fraction (how much of the viewport shifted) by the distance fraction (how far elements moved). The score is unitless — not measured in seconds or milliseconds like LCP and INP.
≤ 0.1
Good
0.1–0.25
Needs Improvement
> 0.25
Poor
Why CLS matters for your Shopify store:
- SEO rankings: Google uses CLS as a ranking signal — poor CLS means lower search positions
- Conversions: Layout shifts cause misclicks that frustrate customers and increase bounce rate
- User trust: Pages that jump around feel broken and unpolished
- Mobile impact: CLS is typically worse on mobile where screens are smaller and shifts are more noticeable
The good news? Unlike LCP, which depends partly on Shopify's server infrastructure, CLS is almost entirely within your control. Most CLS fixes involve CSS changes and proper HTML attributes — no server upgrades needed. Want to check your current CLS score? Run a free speed test on your store.
The Easy Fix: Automatic CLS Optimization
Before diving into manual theme edits — there's a faster approach. Thunder Page Speed Optimizer automatically addresses several of the most common causes of high CLS on Shopify stores.
How Thunder reduces CLS:
Smart Script Deferral
Defers third-party app scripts that inject content late, reducing post-load layout shifts
Font Optimization
Implements font-display: swap and preloading to minimize text reflow shifts
Critical CSS Inlining
Inlines above-the-fold CSS to prevent late-arriving stylesheets from causing shifts
Dependency-Aware Loading
Manages script load order so app widgets initialize without shifting page content
Average improvement: +27 PageSpeed points
Thunder addresses CLS alongside LCP and INP — most stores see all three Core Web Vitals improve within minutes of enabling optimizations.
Free plan available · No credit card required · 30-second setup · Works with all themes
How to Measure Your Shopify Store's CLS Score
Before fixing CLS, you need to know your current score and identify exactly which elements are shifting. Here are the best tools for Shopify stores:
PageSpeed Insights (Recommended Starting Point)
Go to pagespeed.web.dev or use our free Shopify speed test. Look at two sections:
- Field data (top) — Real user CLS from Chrome users over 28 days. This is what Google uses for rankings.
- Lab data — Simulated CLS from a controlled test. Only captures load-time shifts, not post-load.
Scroll to the Diagnostics section and look for "Avoid large layout shifts" — it lists every element that shifted and its individual contribution to CLS.
Chrome DevTools Performance Panel
Open DevTools (F12), go to the Performance tab, check "Web Vitals," and record a page reload. The timeline shows a Layout Shifts track with purple bars highlighting every shift event:
- Click any shift marker to see exactly which element moved and by how much
- Larger diamonds indicate bigger shifts — focus on those first
- The Summary panel shows the shift score, affected elements, and timing
Pro tip: Use the Live Metrics view in the Performance Panel to interact with your page and catch post-load CLS issues that only appear during scrolling or clicking.
Google Search Console
Under Experience → Core Web Vitals, you'll see how your pages perform across all three metrics. Search Console groups pages by status (Good / Needs Improvement / Poor) and uses real user data. It's the best tool for tracking CLS improvements across your entire site over time. Remember: the data uses a 28-day rolling average, so improvements take about a month to fully reflect.
JavaScript Performance Observer (Advanced)
For debugging specific shifts, paste this into your browser console while browsing your store:
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (!entry.hadRecentInput) {
console.log('CLS shift:', entry.value.toFixed(4));
for (const source of entry.sources) {
console.log(' Element:', source.node);
}
}
}
}).observe({ type: "layout-shift", buffered: true }); This logs every layout shift as it happens, including the specific DOM element that moved. Scroll through your page to catch post-load shifts that PageSpeed Insights misses.
Fix #1: Add Dimensions to All Images
Images without explicit width and height attributes are the #1 cause of CLS, accounting for approximately 60% of all layout shift issues. When the browser first renders your page, it doesn't know how tall an image will be — so it renders with zero height. When the image loads, everything below it jumps down.
The Problem
Many Shopify themes output images without dimensions, especially in product grids, collection pages, and blog post featured images. The browser allocates zero space, then shifts content when images arrive.
The Fix: width and height Attributes
Add width and height attributes to every <img> tag. This tells the browser the aspect ratio so it can reserve the correct space before the image loads:
<!-- ❌ Bad: No dimensions → CLS -->
<img src="product.jpg" alt="Product photo">
<!-- ✅ Good: Dimensions set → No CLS -->
<img src="product.jpg" alt="Product photo" width="800" height="600">
Then in your CSS, ensure images scale responsively without breaking the aspect ratio:
img {
max-width: 100%;
height: auto;
} Shopify Liquid: Use image_tag
If you're editing theme Liquid files, the easiest approach is to use Shopify's image_tag filter, which automatically adds width and height attributes:
{{ product.featured_image | image_url: width: 800 | image_tag }}
This outputs an <img> tag with proper width, height, and srcset attributes — eliminating CLS from product images automatically.
Alternative: CSS aspect-ratio
For image containers where you want a fixed aspect ratio regardless of the actual image dimensions (like product grids), use the CSS aspect-ratio property:
.product-card__image {
aspect-ratio: 1 / 1; /* Square */
width: 100%;
object-fit: cover;
}
This is especially useful for collection grids where you want uniform card heights — the browser reserves space for the exact aspect ratio, and object-fit: cover crops the image to fit.
Fix #2: Reserve Space for App-Injected Content
Shopify apps that inject content into your storefront — review stars, trust badges, announcement bars, payment icons, inventory counters — are the second biggest cause of CLS. These apps load via JavaScript, meaning their content appears after the page has already rendered. When the content pops in, everything around it shifts.
App Blocks: Use min-height
For apps that use Theme App Extensions (app blocks), you can set a minimum height on their container using Custom CSS in the Theme Editor. Select the app block's parent section, then add:
/* Reserve space for app block content */
.shopify-app-block {
min-height: 24px; /* Adjust to match your app's rendered height */
}
Measure the actual rendered height of the app's content (use DevTools → inspect element → check the height), then set min-height to that value. Even if the exact height varies, setting a close minimum reduces the shift distance significantly.
Non-App-Block Scripts: CSS Workarounds
Some apps still inject content via the older ScriptTag API (not app blocks). These are harder to fix. You'll need to identify the injected element's selector and reserve space with CSS. For example, if a review widget injects above your product description:
/* Reserve space for a review widget that loads late */
:not(.review-widget-wrapper) + .product__description {
margin-top: 80px; /* Height of the review widget */
} This approach is fragile — if the app changes its HTML structure or you move the widget, the CSS breaks. A more maintainable alternative: move the app content below the fold where shifts have less impact on CLS, or switch to an app that supports app blocks.
Announcement Bars and Banners
Announcement bars that appear at the top of the page are a classic CLS source. When they load late, every element on the page shifts down. Two approaches:
- Render server-side: Use your theme's built-in announcement bar (rendered in Liquid, not JavaScript) instead of a third-party app
- Reserve the space: Add a fixed-height placeholder in your theme's header that matches the banner height
/* Reserve 40px for announcement bar */
.header-wrapper {
padding-top: 40px;
} Fix #3: Optimize Web Font Loading to Prevent CLS
Web fonts cause CLS through a phenomenon called FOUT (Flash of Unstyled Text). When your custom font hasn't loaded yet, the browser displays text in a fallback system font. When the custom font arrives, the text re-renders — and because the two fonts have different metrics (character width, line height, letter spacing), the text shifts, pushing surrounding content around.
Step 1: Use font-display: swap (or optional)
In your @font-face declarations, set font-display: swap to show fallback text immediately, then swap to the custom font when ready:
@font-face {
font-family: 'YourBrandFont';
src: url('/fonts/brand-font.woff2') format('woff2');
font-display: swap;
font-weight: 400;
}
For minimal CLS, font-display: optional is even better — it only uses the custom font if it loads within ~100ms, otherwise sticks with the fallback. No font swap means no CLS, but your custom font may not display on slow connections.
Step 2: Preload Critical Fonts
Add preload hints for your most important font files in your theme's <head>:
<link rel="preload" href="/fonts/brand-font.woff2" as="font" type="font/woff2" crossorigin> Only preload 1–2 fonts (your primary heading and body font). Preloading too many fonts wastes bandwidth and can slow down other critical resources.
Step 3: Match Fallback Font Metrics
The CSS size-adjust, ascent-override, and descent-override properties let you tweak your fallback font to match your custom font's metrics — minimizing the shift when the swap happens:
@font-face {
font-family: 'BrandFallback';
src: local('Arial');
size-adjust: 105%;
ascent-override: 95%;
descent-override: 22%;
line-gap-override: 0%;
} Tools like Fallback Font Generator can calculate these override values for your specific fonts. This is one of the most impactful CLS fixes for font-heavy Shopify themes. For a deeper dive into font performance, see our complete Shopify speed optimization guide.
Fix #4: Prevent Late-Arriving CSS Shifts
When CSS loads after the page has already started rendering, the browser re-calculates layouts — causing content to jump as new styles apply. This commonly happens with:
- CSS files loaded via
@importwithin other CSS files (chained loading) - Third-party app stylesheets that load asynchronously
- JavaScript-injected styles (common with React/Vue components in themes)
The Fix: Inline Critical CSS
Inline the CSS needed for above-the-fold content directly in your <head>. This ensures the browser has all the styles it needs for the initial render — no shifts from late-arriving stylesheets.
Manually extracting and inlining critical CSS is complex and error-prone. This is where Thunder shines — it automatically identifies and inlines critical CSS for each page type, then defers the rest. The result: zero CSS-related layout shifts without any manual work.
Avoid CSS @import
If your theme or apps use @import inside CSS files, replace them with <link> tags in the HTML. @import creates a waterfall — the browser downloads CSS, discovers the import, then downloads more CSS. Each step risks a layout recalculation and shift. Learn more about fixing render-blocking resources on Shopify.
Fix #5: Use CSS Transforms Instead of Layout-Triggering Animations
Animations that change an element's top, left, width, height, margin, or padding trigger layout recalculations that count as CLS. Instead, use transform and opacity — these are handled by the GPU compositor and don't cause layout shifts.
/* ❌ Bad: Triggers layout shift */
.slide-in {
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { margin-left: -100px; }
to { margin-left: 0; }
} /* ✅ Good: No layout shift */
.slide-in {
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { transform: translateX(-100px); }
to { transform: translateX(0); }
} Audit your theme's CSS for any animations using layout-triggering properties. Common culprits: slide-in announcement bars, expanding navigation menus, and product image zoom effects. Also check any third-party scripts that add animation effects.
Fix #6: Stabilize Slideshows and Carousels
Hero slideshows and product carousels are a frequent source of CLS. The carousel JavaScript initializes after page load, calculates slide dimensions, and rearranges elements — often causing visible shifts.
Reserve the Slideshow Height
Set a fixed aspect ratio or min-height on your slideshow container so the browser reserves the correct space before JavaScript initializes:
.slideshow-container {
aspect-ratio: 16 / 9; /* Or your preferred ratio */
width: 100%;
overflow: hidden;
background-color: #f5f5f5; /* Prevent flash of empty space */
} Render the First Slide Server-Side
Most modern Shopify themes (like Dawn) render the first slide in Liquid so it displays immediately, then JavaScript takes over for transitions. If your theme only renders slides via JavaScript, consider restructuring to show the first slide in HTML/Liquid — this eliminates the initial shift entirely.
Consider removing the slideshow entirely. Multiple studies show that hero slideshows often underperform a single static hero image in both conversions and performance. If your slideshow is causing stubborn CLS issues, a well-designed static hero may actually improve both your conversions and your Core Web Vitals.
Manual CLS Fixes vs Thunder: Side-by-Side Comparison
Here's how the manual approach compares to letting Thunder handle CLS optimization automatically:
| CLS Fix | Manual Approach | Thunder Approach |
|---|---|---|
| Image dimensions | Edit Liquid templates, add width/height to every img tag | Smart image handling with responsive sizing |
| App-injected content | Identify each app's DOM, write custom CSS min-height rules | Defers non-critical scripts to prevent post-load shifts |
| Font loading | Edit @font-face rules, add preload hints, calculate size-adjust values | Automatic font-display optimization and preloading |
| Critical CSS | Manually extract and inline above-the-fold CSS | Automated critical CSS extraction and inlining |
| Time to implement | 2–8 hours (varies by theme complexity) | 30 seconds (one-click install) |
| Maintenance | Must redo after theme updates or adding new apps | Adapts automatically to theme and app changes |
Manual fixes give you full control but require ongoing maintenance. Thunder handles the most impactful optimizations automatically and adapts as your store changes. Many merchants combine both: install Thunder for the heavy lifting, then apply manual fixes for theme-specific issues. See Thunder pricing plans.
Frequently Asked Questions
What is a good CLS score for Shopify?
Google considers a CLS score under 0.1 as 'good,' 0.1–0.25 as 'needs improvement,' and over 0.25 as 'poor.' Most well-optimized Shopify stores can achieve CLS under 0.1 on desktop and under 0.15 on mobile. Unlike LCP, which is limited by Shopify's infrastructure, CLS is almost entirely within your control through proper image sizing, CSS techniques, and font loading strategies.
What causes high CLS on Shopify stores?
The most common causes of high CLS on Shopify are: images without explicit width and height attributes (accounting for ~60% of CLS issues), dynamically injected content from third-party apps (review widgets, announcement bars, payment badges), late-loading web fonts that cause text to reflow (FOUT), CSS that arrives after initial render, and slideshow or carousel components that resize during initialization.
Does CLS affect SEO rankings?
Yes. CLS is one of Google's three Core Web Vitals (alongside LCP and INP) that serve as ranking signals. Pages with 'good' CLS scores (under 0.1) receive a ranking advantage over pages with poor visual stability. In ecommerce, CLS also directly impacts conversions — layout shifts can cause users to click wrong buttons, abandon carts, or leave the site entirely.
Can Shopify apps cause CLS issues?
Absolutely. Third-party Shopify apps are one of the biggest causes of CLS. Apps that inject content into your storefront — review widgets, trust badges, announcement bars, countdown timers, and upsell popups — often load via JavaScript after the page has rendered. When their content appears, it pushes existing elements around. Thunder helps by deferring non-critical app scripts and managing their loading sequence.
How do I find what's causing CLS on my Shopify store?
Use Google PageSpeed Insights (pagespeed.web.dev) — scroll to the 'Avoid large layout shifts' diagnostic to see exactly which elements shifted and by how much. Chrome DevTools' Performance panel also shows layout shifts in a dedicated track. For field data, check Google Search Console under Experience → Core Web Vitals. You can also use our free speed test tool at thunderpagespeed.com/tools/speed-test/ for a quick check.
Does fixing CLS require coding knowledge?
Some CLS fixes are simple (adding width/height to images), while others require CSS and Liquid knowledge (reserving space for dynamic content, implementing font-display strategies). If you're not comfortable editing theme code, Thunder Page Speed Optimizer handles the most impactful CLS fixes automatically — including font optimization, script deferral to prevent late-injected shifts, and smart resource loading.
Why is my CLS worse in Google Search Console than in PageSpeed Insights?
PageSpeed Insights lab data only measures CLS during initial page load. Google Search Console uses real user data (CrUX) that captures CLS throughout the entire page lifecycle — including shifts from scrolling, lazy-loaded content, and user interactions. Post-load CLS from app widgets, cookie banners, and dynamically loaded sections often accounts for the difference.