CLS is the most irritating of the three Core Web Vitals because you can actually feel it happening. You're reading an article, you're three paragraphs in, and suddenly the text jumps down because a banner loaded above it. Or you've got your finger hovering over the "Cancel" button, and bam, an ad pops in and you accidentally hit "Buy Now" instead.
I've literally watched people rage-close tabs because of layout shift. And the thing is, it's usually not that hard to fix. Most CLS issues come from a handful of predictable causes that have pretty straightforward solutions.
What CLS Actually Measures
Cumulative Layout Shift measures how much visible content moves around during the entire lifespan of a page. It's not about speed. It's about stability. A page can load in 1.2 seconds but still have terrible CLS if elements keep jumping around after they appear.
CLS Thresholds
Good: Under 0.1
Needs Improvement: 0.1 to 0.25
Poor: Over 0.25
Google uses a "session window" approach to calculate CLS. It groups bursts of layout shifts within 5 seconds (with 1-second gaps) and takes the largest window. A single big shift hurts more than several tiny ones spread apart.
Important thing to note: CLS doesn't count shifts that happen within 500ms of a user interaction (like a click or tap). So if someone clicks a button and a dropdown opens, that shift doesn't count. Google understands that users expect the page to change when they do something.
Cause #1: Images Without Dimensions (The Classic)
This is the number one CLS offender across the entire internet. When you include an image in your HTML without width and height attributes, the browser has no idea how big it's going to be until it downloads the image. So it starts with zero space reserved, then suddenly the image loads and everything below it gets shoved down.
The Fix
Always set width and height on every <img> tag:
<!-- Bad: browser doesn't know the size until download -->
<img src="photo.webp" alt="Product shot">
<!-- Good: browser reserves space immediately -->
<img src="photo.webp" alt="Product shot"
width="800" height="600">
The browser uses these dimensions to calculate the aspect ratio and reserve the right amount of space before
the image even starts downloading. Your image can still be responsive with CSS
(width: 100%; height: auto), and the aspect ratio will be maintained.
Modern CSS Alternative
If you can't set width/height on every image (some CMS systems make it difficult), you can use the CSS
aspect-ratio property:
img { aspect-ratio: 16/9; width: 100%; height: auto; }
This works the same way. The browser reserves space at a 16:9 ratio before the image loads.
Cause #2: Ads and Embeds That Load Late
Ad networks are notorious for causing layout shift. The ad slot starts as empty space (or no space at all), the ad JavaScript loads whenever it feels like it, and then suddenly a 300px tall banner appears and pushes your entire article down.
The Fix
Reserve space for your ad slots using CSS min-height:
.ad-container {
min-height: 250px; /* Standard medium rectangle */
width: 300px;
background: #f1f5f9; /* Light placeholder background */
}
.ad-container-leaderboard {
min-height: 90px; /* Standard leaderboard */
width: 728px;
background: #f1f5f9;
}
The idea is simple: always reserve the space, whether the ad loads or not. If the ad doesn't load, you've got a grey rectangle. If it does load, it fills the reserved space without pushing anything around. Not glamorous, but it stops the layout shift dead.
Same goes for any third-party embed. YouTube iframes, Twitter embeds, newsletter forms. If it loads asynchronously, it needs a container with a reserved size.
Cause #3: Web Fonts Causing Text Reflow
Here's one a lot of people miss. When a custom font loads, it might have different character widths than the
fallback system font. The browser initially renders text in the system font (if you're using
font-display: swap), and then when the custom font arrives, it re-renders all the text. If the
new font is wider or taller, everything shifts.
The Fix
Use font-display: swap with a size-adjusted fallback. The size-adjust property
tells the browser to scale the fallback font to match your custom font's dimensions as closely as possible:
@font-face {
font-family: 'Inter';
src: url('inter.woff2') format('woff2');
font-display: swap;
}
/* Size-adjusted fallback */
@font-face {
font-family: 'Inter-Fallback';
src: local('Arial');
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
There's a nice tool at screenspan.net/fallback that generates the exact override values for your specific font. It takes like 30 seconds. The result is that when your custom font loads and replaces the fallback, the text barely moves at all.
Cause #4: Dynamic Content Injected Above
Cookie banners. Notification bars. "Download our app" prompts. Anything that gets inserted at the top of the page after the initial render will push all content below it down, causing a massive layout shift.
The Fix
Two options, depending on your situation:
Option A: Reserve space for the banner from the start. If you know a cookie banner will show (and GDPR says you need one for EU users), add a placeholder div with the right height at the top of the page.
Option B: Use CSS transform instead of changing layout properties. A transform
animation doesn't cause layout shift because the element is moved in its own compositing layer:
/* Bad: pushes content down, causes CLS */
.banner {
position: relative;
top: -60px;
animation: slideDown 0.3s forwards;
}
@keyframes slideDown { to { top: 0; } }
/* Good: no layout shift */
.banner {
transform: translateY(-60px);
animation: slideIn 0.3s forwards;
}
@keyframes slideIn { to { transform: translateY(0); } }
Transforms, opacity changes, and filter changes don't trigger layout recalculations. That's the key difference.
Cause #5: Iframes Without Dimensions
Same problem as images. YouTube embeds, Google Maps, payment forms, any iframe that doesn't have explicit dimensions will cause a shift when it loads.
<!-- Bad -->
<iframe src="https://www.youtube.com/embed/abc123"></iframe>
<!-- Good -->
<iframe src="https://www.youtube.com/embed/abc123"
width="560" height="315"
style="aspect-ratio: 16/9; width: 100%; height: auto;">
</iframe>
Cause #6: CSS Animations That Change Layout Properties
If you animate height, width, padding, or margin, the
browser has to recalculate the layout of everything around that element. That counts as a layout shift.
Stick to animating transform and opacity. These are GPU-composited and don't affect
layout at all. If you need to expand something, use transform: scaleY() instead of
height.
How to Debug CLS Issues
Finding CLS issues can be tricky because they often happen during page load when you're not really watching. Here are the best ways to catch them:
Chrome DevTools Performance Panel
- Open DevTools, go to the Performance tab
- Hit Record, reload the page, then stop recording
- Look for red markers in the "Experience" row. Each one is a layout shift
- Click a shift to see exactly which element moved and by how much
Web Vitals Chrome Extension
Install the Web Vitals extension. It shows your CLS score in real time as you browse and highlights which elements are causing shifts.
VitalsFixer
Run your URL through VitalsFixer and it'll flag CLS issues in your punch-list with specific elements and recommendations. Faster than manually digging through DevTools if you want a quick overview.
The CLS Fix Checklist
Go Through These Before You Deploy
1. Every image and video has width and height attributes
2. Ad containers have min-height set
3. Fonts use font-display: swap with size-adjusted fallback
4. No content injected above existing content without reserved space
5. All iframes have dimensions set
6. Animations use transform/opacity, not height/width/margin
7. Cookie banners use transform animations or reserved space
8. Dynamic lists (infinite scroll, load more) add content below, not above
FAQ
What's a good CLS score?
Under 0.1. Google measures this with real field data at the 75th percentile. So even if most of your users have good CLS, if 25% of them see bad layout shifts, you'll fail.
Does CLS affect SEO rankings?
Yes. CLS is one of Google's three Core Web Vitals, which are ranking signals. A site with CLS over 0.25 may be penalized in search rankings compared to a competitor with CLS under 0.1, all else being equal.
Why does my CLS look fine in Lighthouse but bad in field data?
Lighthouse runs on a fast machine with no real-world conditions like slow ad networks, delayed third-party scripts, or slow connections. Real users on slower phones with spotty connections see ads load late, fonts swap later, and images paint slower. That's why field CLS is almost always worse than lab CLS.
Do carousels cause CLS?
They can, if the carousel changes height as slides load. Set a fixed height on the carousel container and make sure all slide images use the same aspect ratio. Auto-rotating carousels that shift content position on their own (without user interaction) can also trigger CLS.
Check Your CLS Score Right Now
Run your site through VitalsFixer. We'll tell you exactly which elements are shifting and how to stop them.
Analyze My Site Free →CLS driving you crazy? Let us handle it.
Our engineers fix layout shift, image optimization, and all three Core Web Vitals. 48-hour turnaround. Money-back guarantee if your scores don't improve.
View Expert Fix Service →