Key Takeaways
- CLS measures unexpected layout shifts — elements moving around while the page loads
- The threshold is 0.1 — anything over causes a “Poor” Core Web Vitals rating
- The #1 cause is images and iframes without explicit width and height attributes
- Web font loading (FOUT/FOIT) is the second most common cause on WordPress sites
- CLS is measured across the entire page lifetime in the field, not just initial load — dynamic content and ads can fail you even if initial load is fine
What CLS measures and why it’s the sneakiest Core Web Vital
Cumulative Layout Shift measures the visual stability of your page — how much elements move around unexpectedly while the page loads and throughout the user’s session. It’s one of three Core Web Vitals that Google uses for ranking, and it’s the one that catches the most site owners off guard.
Unlike LCP, which is a raw speed metric, CLS measures user frustration. When a button moves just as you’re about to click it, or text jumps down because an image loaded above it, that’s a layout shift. Users hate it, and Google knows it. As part of our complete Core Web Vitals guide, CLS is often the most overlooked metric because it doesn’t feel like a “speed” issue — but it affects rankings just the same.
The threshold is 0.1. A score of 0 means nothing shifted. A score of 0.1 means some minor shifts that users barely notice. Anything over 0.25 is “Poor” and will trigger a CWV failure in Search Console. CLS and LCP often share root causes — images that are slow to load AND lack dimensions will hurt both metrics simultaneously.
How to diagnose CLS
CLS is the trickiest Core Web Vital to diagnose because CLS is measured differently in lab vs field. Lab tools (Lighthouse, PageSpeed Insights lab section) only measure the initial page load. Field data (CrUX, Search Console) measures the entire session — including shifts caused by scrolling, user interaction, lazy loaded content, and dynamically injected elements.
This is why you can score 0.01 CLS in Lighthouse and still fail CLS in Search Console. Real users scroll. They trigger lazy loaded images. They interact with expandable sections. Ads load below the fold. All of these can cause layout shifts that lab tests never see.
To diagnose CLS properly:
- Chrome DevTools → Performance tab — Record a page load and scroll. Look for “Layout Shift” entries in the Experience row. Each one shows the affected elements and the shift score.
- Layout Shift Regions — In DevTools, open the Rendering panel (Ctrl+Shift+P → “Rendering”) and enable “Layout Shift Regions”. Blue highlights appear on elements that shift.
- web-vitals.js attribution build — Add the attribution build of web-vitals.js to your site to collect CLS data from real users, including which elements caused the shifts.
- Search Console — The CWV report groups URLs with similar CLS issues. Start with the URL groups showing “Poor” CLS.
Fix #1: Add width and height to all images and iframes
This is the single highest-impact CLS fix. When an <img> tag doesn’t have width and height attributes, the browser doesn’t know how much space to reserve. It initially renders the image at 0 height, then shifts everything down when the image loads. Images without dimensions cause layout shift — it’s the #1 CLS offender we see across WordPress sites.
The fix is straightforward:
<!-- BAD — causes CLS -->
<img src="photo.webp" alt="Photo" loading="lazy">
<!-- GOOD — prevents CLS -->
<img src="photo.webp" alt="Photo" width="800" height="600" loading="lazy">
WordPress has added width and height to core image output since version 5.5, but there are gaps:
- Images inserted via page builders (Elementor, Beaver Builder) may not include dimensions
- Images in custom theme templates may lack attributes
- Background images set via CSS have no dimension mechanism — you need to set the container’s
aspect-ratio - Iframes (YouTube embeds, Google Maps) rarely include dimensions
For iframes, always add width and height, or wrap them in a container with a fixed aspect ratio:
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
}
.video-wrapper iframe {
width: 100%;
height: 100%;
}
Fix #2: Fix web font loading
Web fonts are the second most common CLS cause on WordPress. When a custom font loads, text rendered in the fallback system font re-renders in the custom font, causing a layout shift. This is called FOUT (Flash of Unstyled Text) or FOIT (Flash of Invisible Text).

The fixes:
Use font-display: swap with a matched fallback. This tells the browser to show text in a system font immediately, then swap when the custom font loads. The key is matching the fallback font’s metrics (x-height, character width) to the custom font so the swap causes minimal shift:
@font-face {
font-family: 'CustomFont';
src: url('custom.woff2') format('woff2');
font-display: swap;
/* Use a size-adjusted fallback */
size-adjust: 105%;
ascent-override: 90%;
}
body {
font-family: 'CustomFont', -apple-system, BlinkMacSystemFont, sans-serif;
}
Preload critical fonts. Add <link rel="preload" as="font" type="font/woff2" href="custom.woff2" crossorigin> in your <head> to start downloading fonts immediately instead of waiting for CSS to be parsed.
Consider system fonts entirely. The fastest font is the one already on the user’s device. A system font stack eliminates font-related CLS completely. This site uses system fonts — no layout shift, no font loading delay.
Fix #3: Reserve space for dynamic content
Dynamically injected content — ads, embeds, cookie notices, chat widgets — can cause massive layout shifts because the browser didn’t know about them when it first laid out the page.

Ads: Reserve the exact ad slot dimensions with CSS before the ad script loads:
.ad-slot {
min-height: 250px; /* Match ad unit height */
width: 300px;
background: var(--bg-card);
}
Embeds (YouTube, Twitter, Instagram): Use aspect-ratio containers rather than letting the embed script determine the dimensions.
Cookie consent banners: If your cookie banner pushes content down (top banner), it causes CLS. Use a bottom-positioned overlay instead, or use a banner that doesn’t shift page content (CSS position: fixed).
Chat widgets: Widgets like Intercom, Drift, or HubSpot inject a button and iframe into the DOM after page load. These are typically positioned fixed and don’t cause CLS, but some implementations do. Test with Layout Shift Regions enabled to verify.
Fix #4: Avoid inserting content above existing content
Any content inserted above already-visible elements pushes everything down. Common WordPress culprits:
- Sticky/fixed headers that change height — A header that starts at 80px and shrinks to 60px on scroll shifts content below it
- Notification bars — “Free shipping on orders over £50” bars inserted at the top of the page
- WooCommerce notices — “Item added to cart” messages that appear at the top of the page
- WordPress admin bar — The 32px admin bar shifts all content when loaded (only affects logged-in users)
The principle: never insert content above already-visible content. Use overlays, bottom-positioned elements, or reserve space in the initial layout.
WordPress-specific CLS issues
Elementor. Elementor’s frontend JavaScript initialises animations, sliders, and carousels after page load. Elements with entrance animations shift from their initial position to their final position, causing CLS. Disable entrance animations on above-fold elements, or ensure they’re in their final position in the initial HTML. Plugins that help prevent CLS include Perfmatters and Asset CleanUp, which can disable Elementor’s frontend JS on pages that don’t use its interactive features.
WooCommerce AJAX cart. When “Add to Cart” triggers an AJAX update, the cart icon in the header may resize, or a mini-cart dropdown may shift the header layout. Ensure your cart element has a fixed size regardless of item count.
Lazy loaded images above the fold. If an image is in the viewport on load but has loading="lazy", it starts at 0 height and shifts content when it loads. WordPress auto-applies lazy loading — make sure above-fold images are excluded.
Google Fonts loaded via @import in CSS. Some themes load Google Fonts with @import inside the stylesheet, which creates a chain: CSS download → CSS parsing → font download → text re-render (shift). Switch to <link rel="preload"> in the <head> instead.
Testing CLS properly
The critical thing to understand about CLS testing: lab tests and field data often disagree, and field data is what Google uses for rankings.
Lab tests (Lighthouse, PSI) only measure CLS during initial page load. They don’t scroll. They don’t click. They don’t wait for lazy loaded content. A site can score 0.00 CLS in the lab and 0.3 CLS in the field.
To test CLS properly:
- Record a full Performance trace including scrolling through the entire page
- Check Search Console field data for the real-world CLS score
- Test on actual mobile devices (not just Chrome DevTools simulation)
- Wait at least 28 days after making changes for CrUX data to update
- Use the web-vitals.js attribution build to identify which elements cause shifts in production
What is a good CLS score?
Google considers CLS under 0.1 as “Good”. We target 0.05 or lower for our clients. A well-optimised WordPress site should achieve CLS under 0.01 — meaning virtually no visible layout shifts. The most common cause of CLS over 0.1 is images without width and height attributes.
Why does my CLS pass in PageSpeed but fail in Search Console?
PageSpeed Insights lab test only measures CLS during initial page load — it doesn’t scroll, click, or interact with the page. Search Console uses field data from real users who scroll, trigger lazy loaded content, interact with dynamic elements, and encounter ads loading. The difference is almost always caused by below-fold content shifts that lab tests never see.
Do ads cause CLS?
Yes, ads are one of the most common CLS causes. When an ad loads, it takes up space that wasn’t reserved in the initial layout, pushing surrounding content. The fix: always reserve the exact ad slot dimensions with CSS (min-height and width) before the ad script loads. This reserves the space even if the ad takes several seconds to fill it.
Can web fonts cause CLS?
Yes. When a custom font loads, text re-renders from the fallback system font to the custom font. If the fonts have different metrics (character width, line height, x-height), this causes visible layout shifts. The fix: use font-display: swap with a size-adjusted fallback, preload critical fonts, or switch to system fonts entirely.
Does WordPress cause CLS by default?
WordPress core is reasonably CLS-safe — it adds width and height to images and has improved lazy loading handling. The CLS issues come from themes (especially page builders like Elementor), plugins (ad managers, popup builders, chat widgets), and custom development that doesn’t account for layout stability. A clean WordPress installation with a well-coded theme typically has CLS under 0.01.
CLS still failing in Search Console?
We eliminate every layout shift — guaranteed
Our free audit identifies every source of CLS on your WordPress site. We fix them all as part of our performance optimisation.