What Causes Cumulative Layout Shift?
What Causes Cumulative Layout Shift? 6 Causes and Fixes
Key Takeaway: Cumulative Layout Shift (CLS) measures how much your page content unexpectedly moves while loading. A good CLS score is 0.1 or less. The most common causes are images without dimensions, web fonts swapping late, and dynamically injected content like ads and cookie banners. All of them are fixable without a developer in most cases.
CLS is the Core Web Vital that frustrates users the most. You go to click a button — it jumps. You start reading an article — the text shifts down. You tap a link — an ad loads above it and you hit the wrong thing.
That frustration is exactly what Google is measuring. And if your CLS score is above 0.1, it is affecting both your user experience and your search rankings.
If you have already checked your scores and confirmed CLS is your problem, this article covers every major cause and the exact fix for each one. If you are still diagnosing which Core Web Vital is failing on your site, start with the complete Core Web Vitals fix guide first.
What is CLS and how is it measured?
CLS measures the total amount of unexpected layout shift that happens during the entire lifespan of a page visit. It is not a time measurement — it is a score calculated from two factors:
- Impact fraction — how much of the visible viewport was affected by the shift
- Distance fraction — how far the element moved as a proportion of the viewport
The formula is: CLS score = impact fraction × distance fraction
Every individual shift produces a score. These are grouped into session windows and the worst session window becomes your final CLS score.
| CLS Score | Rating |
|---|---|
| 0.1 or less | Good |
| 0.1 to 0.25 | Needs improvement |
| Above 0.25 | Poor |
Google measures CLS at the 75th percentile of real user sessions. A single large shift experienced by most of your visitors will reliably push your score into the Poor range.
Why does CLS matter beyond rankings?
Before diving into causes and fixes, it is worth understanding what bad CLS actually costs you.
Google's research shows that pages with poor CLS have significantly higher bounce rates. Users who experience layout shifts are more likely to leave immediately — not because the page is slow, but because it feels broken and untrustworthy.
For ecommerce sites specifically, layout shifts near the add-to-cart button or checkout flow are directly tied to abandoned sessions. Users who accidentally tap the wrong element due to a shift rarely come back to complete the purchase.
If your site is also struggling with slow page speed on Blogger or has recently been deindexed by Google, poor CLS compounds those problems — Google's Page Experience signal scores all three Core Web Vitals together.
How to find what is causing your CLS
Before fixing CLS you need to know exactly what is shifting and when.
Method 1: PageSpeed Insights
- Go to pagespeed.web.dev
- Paste your URL and run the analysis
- Scroll to the Diagnostics section
- Look for "Avoid large layout shifts" — it lists the specific elements causing shifts and their individual CLS contribution scores
Method 2: Chrome DevTools Layout Shift Regions
- Open Chrome DevTools (F12)
- Go to the Rendering panel (click the three-dot menu → More tools → Rendering)
- Enable Layout Shift Regions
- Reload your page — elements that shift will flash blue on screen
This is the fastest way to visually see exactly what is moving and when.
Method 3: Google Search Console
- Go to Search Console → Experience → Core Web Vitals
- Click into the CLS issues report
- This groups failing URLs by issue type and shows you which pages have the worst CLS across real user sessions
The 6 most common causes of CLS
Cause 1: Images without explicit width and height attributes
This is the single most common cause of CLS across the web and the easiest to fix.
When a browser loads a page, it builds the layout before images have finished downloading. If an image does not have explicit width and height attributes, the browser does not know how much space to reserve for it. It allocates zero height. When the image loads, it pushes everything below it down — causing a layout shift.
The fix:
Always add explicit width and height attributes to every <img> element:
html
<img
src="product-image.webp"
width="800"
height="600"
alt="Product name"
>Then in your CSS, make it responsive without breaking the aspect ratio:
css
img {
width: 100%;
height: auto;
}The browser uses the width and height attributes to calculate the correct aspect ratio and reserves the right amount of space before the image loads. Zero layout shift.
For images where you do not know the exact dimensions — user-uploaded content, dynamic product images — use the aspect-ratio CSS property:
css
.image-wrapper {
aspect-ratio: 4 / 3;
width: 100%;
overflow: hidden;
}
.image-wrapper img {
width: 100%;
height: 100%;
object-fit: cover;
}This is also directly relevant to your LCP score. Fixing image dimensions often improves both CLS and LCP at the same time. Read more about what counts as a good LCP score and how image optimisation connects to both metrics.
Cause 2: Web fonts causing text reflow (FOUT and FOIT)
Web fonts are the second most common CLS cause — and the most overlooked.
When your page loads, the browser initially renders text using a system fallback font (Arial, Georgia, Times New Roman). When your custom web font finishes downloading, the browser swaps it in. If the custom font has different dimensions to the fallback font — different character width, line height, or letter spacing — the text block changes size. Everything around it shifts.
This is called FOUT (Flash of Unstyled Text) when the fallback text is visible, or FOIT (Flash of Invisible Text) when the text is hidden until the font loads.
The fix:
Option A — font-display: optional (zero CLS, safest)
css
@font-face {
font-family: 'YourFont';
src: url('/fonts/yourfont.woff2') format('woff2');
font-display: optional;
}optional tells the browser: use the custom font only if it is already cached. If not, use the fallback and do not swap. First-time visitors see the fallback font. Return visitors see the custom font. Zero layout shift either way.
Option B — font-display: swap with size-adjust (custom font always loads)
If you need the custom font to load for all users, use font-display: swap paired with size-adjust to match the fallback font's metrics as closely as possible:
css
@font-face {
font-family: 'YourFont';
src: url('/fonts/yourfont.woff2') format('woff2');
font-display: swap;
size-adjust: 105%;
ascent-override: 90%;
descent-override: 10%;
}The size-adjust, ascent-override, and descent-override values adjust the fallback font's dimensions to match your custom font. When the swap happens, the text block barely moves because both fonts take up the same space.
Also preload your key fonts:\
html
<link
rel="preload"
href="/fonts/yourfont.woff2"
as="font"
type="font/woff2"
crossorigin
>Preloading gives the font a head start. The earlier it loads, the less likely a late swap will cause a visible shift.
Cause 3: Dynamically injected content above existing content
This is the CLS cause that is hardest to avoid on ad-supported and marketing-heavy sites.
When content is injected above existing page content after the initial render — a cookie consent banner, a promotional bar, a chat widget, a newsletter popup — everything below it shifts down. The later the injection happens, the more users experience the shift.
Common culprits:
- Cookie consent banners loaded via JavaScript after page load
- Sticky promotional bars injected by marketing tools
- Chat widgets (Intercom, Drift, Tidio) that push page content down
- Ad networks inserting ad units into the content flow
- Email signup bars injected by email marketing plugins
The fixes:
For cookie banners: Use position: fixed or position: sticky so the banner overlays content rather than pushing it down. Most modern cookie consent tools have this as a setting.
For promotional bars: Include the bar in your initial HTML, not injected via JavaScript. If it must be injected, reserve its space with a placeholder element:
css
.promo-bar-placeholder {
min-height: 48px; /* match the bar's height */
width: 100%;
}For ad slots: Always reserve space before the ad loads. Give every ad container a fixed min-height matching the ad unit size:
css
.ad-container {
min-height: 250px; /* standard display ad height */
width: 100%;
}For chat widgets: Most chat widget providers now offer a position: fixed mode that anchors the widget to a corner without affecting page layout. Enable this in your widget settings.
Cause 4: Animations that change layout properties
Not all animations cause CLS — but animations that change layout properties do.
When you animate top, left, right, bottom, width, height, margin, or padding, the browser has to recalculate the entire page layout on every animation frame. This triggers layout shifts and contributes to your CLS score.
Properties that cause CLS when animated:
top,left,right,bottomwidth,heightmargin,paddingfont-size
Properties that do NOT cause CLS:
transform: translateX(),translateY(),scale(),rotate()opacity
The fix:
Replace layout-affecting animations with transform equivalents:
css
/* Bad — causes CLS */
.element {
transition: top 0.3s ease;
}
.element:hover {
top: -10px;
}
/* Good — no CLS */
.element {
transition: transform 0.3s ease;
}
.element:hover {
transform: translateY(-10px);
}transform animations run on the GPU compositor thread. They do not trigger layout recalculation and do not contribute to CLS.
Cause 5: Em beds and iframes without reserved dimensions
Third-party embeds — YouTube videos, Twitter posts, Google Maps, social media widgets — are loaded inside <iframe> elements. If those iframes do not have explicit dimensions, the browser cannot reserve space for them. When they load, they expand and push content down.
The fix:
Always wrap embeds in a container with a fixed aspect ratio:
css
.embed-container {
position: relative;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
height: 0;
overflow: hidden;
}
.embed-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}Or use the modern aspect-ratio approach:
css
.embed-container {
aspect-ratio: 16 / 9;
width: 100%;
}
.embed-container iframe {
width: 100%;
height: 100%;
}For YouTube embeds specifically, use loading="lazy" on iframes below the fold — but never on iframes that are visible on initial page load.
Cause 6: Late-loading JavaScript that modifies the DOM
JavaScript that runs after the page has painted and inserts or moves elements is one of the harder CLS causes to debug.
Common examples include:
- A/B testing scripts that swap content or move elements
- Personalisation scripts that replace generic content with user-specific content
- Lazy-loaded components that expand into the content flow
- JavaScript carousels and sliders that initialise late and resize their container
The fix:
The root solution is to minimise DOM changes after the initial paint. Practically:
- Reserve space for all JavaScript-rendered components before the script runs
- Use
min-heighton containers that will be populated by JavaScript - Initialise JavaScript components before the first paint where possible by moving scripts earlier in the load order
- For A/B testing tools: switch to server-side A/B testing to eliminate client-side DOM modifications entirely
If you are seeing Google Search Console errors alongside poor CLS scores, late-loading JavaScript that modifies the DOM is often a contributing factor to both — particularly on sites running multiple marketing and analytics scripts.
CLS causes by platform
WordPress
The most common WordPress CLS causes are:
- Page builders (Elementor, Divi, WPBakery) that inject layout-affecting scripts after page load
- Theme sliders that initialise late and cause their container to expand
- Cookie consent plugins set to inject a top banner instead of a fixed overlay
- WooCommerce product image galleries that resize on load
- Google Fonts loaded via
@importin CSS instead of<link rel="preload">in<head>
Start with your image dimensions and Google Fonts setup — these two fixes alone resolve CLS for the majority of WordPress sites.
Shopify
Common Shopify CLS causes:
- Product image aspect ratio inconsistency — if product images have different aspect ratios, the image container resizes on load for each product
- App-injected banners — promotional and upsell apps that insert content above the header
- Announcement bars loaded via JavaScript instead of included in the theme's Liquid templates
- Review widgets (Okendo, Loox, Judge.me) that expand their container when reviews load
The fastest Shopify fix is to standardise all product images to the same aspect ratio — 1:1 or 4:3 — and add aspect-ratio to your product image containers in the theme CSS.
Next.js and React
React-specific CLS causes:
- Client-side rendering of above-the-fold content — the server sends an empty shell, JavaScript renders the content, causing a shift
- Dynamic imports that load visible components after the initial paint
- React hydration differences between server-rendered and client-rendered output
- CSS-in-JS solutions that inject styles after the initial render
The primary fix for React CLS is to use server-side rendering (SSR) or static generation for above-the-fold content so the browser receives the correct layout in the initial HTML.
How to prioritise your CLS fixes
Not all CLS causes contribute equally. Use this order:
- Fix image dimensions first — affects the most pages, easiest to implement, often resolves 50–70% of CLS
- Fix web fonts — high impact on text-heavy sites, straightforward CSS changes
- Fix ad slots and dynamic content — critical for ad-supported sites and ecommerce
- Fix animations — quick CSS swap, low effort, immediate improvement
- Fix embeds — important for content-heavy sites with lots of third-party embeds
- Fix JavaScript DOM modifications — most complex, address last
CLS fixes checklist
| Industry | Typical Good LCP | Common Issues |
|---|---|---|
| News and media | 1.5 – 2.2s | Large hero images, ad scripts |
| Ecommerce | 1.8 – 2.5s | High-res product images, app bloat |
| SaaS and software | 1.2 – 2.0s | Usually cleaner, fewer images |
| WordPress blogs | 2.0 – 3.5s | Unoptimised themes, shared hosting |
| Shopify stores | 2.0 – 3.0s | Theme JS, installed apps |
Frequently asked questions
Q1. What is a good CLS score?
A good CLS score is 0.1 or less. This is Google's threshold for the "Good" rating in Core Web Vitals. A score between 0.1 and 0.25 needs improvement. Above 0.25 is Poor and will negatively affect your Page Experience ranking signal.
Q2. What is the most common cause of CLS?
Images without explicit width and height attributes are the single most common cause of CLS across the web. Adding these two attributes to every image element on your site will resolve CLS for the majority of websites.
Q3. Does CLS affect SEO rankings?
Yes. CLS is one of Google's three Core Web Vitals, which are official Page Experience ranking signals. A Poor CLS score can cost you rankings in competitive search results, particularly when competing against pages with similar content quality. For context on how all three vitals connect, see the complete Core Web Vitals guide.
Q4. How do I check my CLS score?
The fastest method is PageSpeed Insights — paste your URL and look at the field data section for your real-user CLS score. You can also check Google Search Console under Experience → Core Web Vitals for a site-wide view of which pages are failing.
Q5. Can fonts cause CLS?
Yes. Web fonts are one of the most common and most overlooked causes of CLS. When a custom font loads and swaps in after the fallback font has already rendered, the text block changes size if the two fonts have different dimensions. Use font-display: optional or pair font-display: swap with size-adjust to prevent this.
Q6. Why is my CLS score different on mobile and desktop?
Google measures CLS separately for mobile and desktop users. Mobile users typically experience higher CLS because mobile connections are slower — meaning more content loads in stages, increasing the chance of late shifts. Ad networks also serve different ad sizes on mobile, which can cause different amounts of shift.
Q7. Does lazy loading cause CLS?
Lazy loading itself does not cause CLS if used correctly. The problem arises when lazy-loaded images do not have explicit dimensions — the browser cannot reserve space for them. Always add width and height attributes to lazy-loaded images. Never apply lazy loading to your LCP element. Learn more about how LCP and lazy loading interact.
Q8. How do I fix CLS on a Blogger site?
On Blogger, the most common CLS causes are images without dimensions and late-loading gadgets (widgets) that inject content into the page after load. Add explicit width and height to all images in your posts, move non-essential gadgets to the footer, and check that your theme does not use layout-affecting animations. If you are also dealing with speed issues on Blogger, see why your Blogger page speed is low for a full walkthrough.
Summary
CLS is caused by content moving unexpectedly after the page has started rendering. The six main causes are images without dimensions, web fonts that swap late, dynamically injected content, layout-affecting animations, unsized embeds, and late-loading JavaScript.
For most sites, fixing image dimensions and font loading resolves the majority of CLS without any developer involvement. Start there, measure again in PageSpeed Insights, then work through the remaining causes in order of contribution.
If your CLS is fixed but LCP or INP is still failing, the complete Core Web Vitals guide covers all three metrics with the same level of detail.
