How to Fix CLS Issues: Cumulative Layout Shift Guide
How to fix CLS issues that hurt rankings β every cause of Cumulative Layout Shift with copy-paste before-and-after code fixes to stabilize your page.

Cumulative Layout Shift (CLS) is the Core Web Vitals metric that measures how much a page's content unexpectedly moves while it loads. You fix CLS by reserving space for every element before it renders β set explicit width and height on images and video, use the CSS aspect-ratio property for responsive containers, pre-allocate ad and embed slots, and load custom fonts with size-adjusted fallbacks. A good CLS score is 0.1 or lower. Get there and your page stops jumping, your users stop misclicking, and Google stops demoting you.
That jumpy load β text reflowing, a button sliding down right as someone taps it, an ad shoving the article half a screen down β is the most fixable Core Web Vital. Almost every shift comes from one root cause: the browser didn't know how big something would be, so it laid the page out twice. Reserve the space up front and the shift disappears. This guide walks through every cause and gives you copy-paste code for each.
What Is Cumulative Layout Shift?
Cumulative Layout Shift quantifies visual stability β how much page content moves unexpectedly during loading. Google calculates each shift by multiplying the impact fraction (how much of the viewport the moving content affects) by the distance fraction (how far it moves relative to the viewport).
CLS is not a single measurement. It captures every unexpected shift across the page's lifecycle using session windows: the largest burst of shifts inside a 5-second window becomes your reported score. So one big ad injection or a hundred tiny font reflows can both wreck your number.
Per Google's official Core Web Vitals thresholds, the score bands are:
| CLS score | Rating | What it means |
|---|---|---|
| 0.1 or less | Good | Visually stable β aim here |
| 0.1 β 0.25 | Needs improvement | Noticeable jumps; fixable |
| Above 0.25 | Poor | Earthquake mode β actively demoted |
One important nuance: not every shift counts against you. Shifts that happen within 500ms of a user interaction (clicking a "load more" button, opening an accordion) are considered expected and excluded. CLS only punishes movement the user didn't ask for.

Why CLS Matters for SEO and Conversions
Google made Core Web Vitals a confirmed ranking signal with the 2021 Page Experience update, and CLS is one of the three pillars alongside LCP and INP. When two pages compete on relevance, the one that's visually stable has an edge β and the effect is sharpest on mobile, where small screens make every shift more violent.
Rankings aside, layout shift is a trust killer. When someone goes to tap "Add to cart" and an ad bumps the page so they hit "Delete account" instead, they don't blame the ad β they blame your site, and they leave. A stable page feels professional and finished; a jumpy one feels broken even when everything works.
CLS sits inside the broader picture of Core Web Vitals and your overall page speed. Run a free SlapMyWeb audit to see which elements on your page actually shift and get the fix code for each.
1. Set Explicit Dimensions on Images and Video
The number-one cause of CLS is media that loads without reserved space. The browser doesn't know an image's size until it downloads, so everything below it jumps down to make room once it arrives.
Always include width and height attributes on every <img> and <video> tag. Modern browsers use those values to compute the aspect ratio and reserve the correct space before the file loads β even when CSS later resizes the element to be responsive.
<!-- Bad: No dimensions β browser can't reserve space -->
<img src="/hero-banner.jpg" alt="Hero banner">
<!-- Good: Explicit dimensions β space reserved before load -->
<img src="/hero-banner.jpg" alt="Hero banner" width="1200" height="600">
<!-- Better: Modern CSS aspect-ratio for fluid layouts -->
<img src="/hero-banner.jpg" alt="Hero banner"
style="width: 100%; height: auto; aspect-ratio: 2/1;">This single fix often eliminates the majority of CLS on a typical site. Pair it with smart image optimization and descriptive alt text and you've handled both stability and accessibility in one pass.
2. Use CSS aspect-ratio for Responsive Containers
For layouts where fixed pixel values won't work, the CSS aspect-ratio property reserves exactly the right space at any viewport width. It's the cleanest fix for video embeds, iframes, and slots whose final content arrives late.
/* Reserve space for video embeds */
.video-container {
width: 100%;
aspect-ratio: 16 / 9;
background-color: #f0f0f0; /* placeholder while loading */
}
/* Reserve space for ad slots */
.ad-slot-leaderboard {
width: 100%;
min-height: 90px; /* standard leaderboard height */
aspect-ratio: 728 / 90;
contain: layout; /* keeps internal changes from shifting siblings */
}
/* Reserve space for dynamic content */
.dynamic-banner {
min-height: 60px;
contain: layout size;
}3. Reserve Space for Ads and Dynamic Content
Ads are CLS killers. They load asynchronously β sometimes seconds after first paint β and push everything below them down. The fix is to pre-allocate space for every ad slot in your initial HTML, before the ad ever requests.
- Build placeholder containers sized to your ad units.
- Use `min-height` rather than
heightso the slot can grow if an ad is taller, but never collapses smaller than expected. - Never inject ads or promo banners above the fold after the initial render β that's the single most damaging shift you can ship.
If you genuinely need late-arriving content near the top, reserve its space in the server-rendered HTML so the slot exists from frame one. This is doubly important for stores β see the Core Web Vitals for e-commerce guide for product-grid and promo-bar patterns that don't shift.
4. Load Fonts with Size-Adjusted Fallbacks
Custom fonts cause a flash and a shift when the browser swaps from a fallback font to your loaded web font, because the two typefaces have different metrics and the text reflows. font-display: swap keeps text visible during load β but you also need a fallback whose dimensions match your web font so the swap barely moves anything.
/* Show text immediately, then swap to the web font */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom-font.woff2') format('woff2');
font-display: swap;
}
/* Size-adjusted fallback to minimize reflow on swap */
@font-face {
font-family: 'CustomFont-Fallback';
src: local('Arial');
size-adjust: 105%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
body {
font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
}The size-adjust, ascent-override, descent-override, and line-gap-override properties tune the fallback to occupy the same vertical space as your real font. When the swap fires, text holds its position instead of nudging surrounding content.

5. Animate with transform and opacity, Not Layout Properties
Animating width, height, top, left, margin, or padding forces the browser to recalculate layout on every frame β and those recalculations register as layout shifts. Animate with transform and opacity instead. Both run on the compositor thread, never touch layout, and never cost you CLS.
/* Bad: animating height triggers layout shift */
.accordion-content {
height: 0;
transition: height 0.3s ease;
}
.accordion-content.open {
height: auto; /* recalculates layout */
}
/* Good: transform + opacity stay off the layout path */
.notification-toast {
transform: translateY(100%);
opacity: 0;
transition: transform 0.3s ease, opacity 0.3s ease;
}
.notification-toast.visible {
transform: translateY(0);
opacity: 1;
}6. Use CSS Containment to Isolate Widgets
The contain property tells the browser that an element's internals won't affect anything outside it. That stops a shift inside a widget from rippling out into the rest of the page.
Apply contain: layout or contain: content to embeds, third-party widgets, comment sections, and any block that hydrates or populates after first paint. Each becomes a sealed box: whatever happens inside stays inside.
.embedded-widget {
contain: content;
}7. Avoid Late Content Injection Above the Fold
Never use JavaScript to insert content above the current scroll position after the page has rendered. The usual offenders:
- Cookie-consent banners that push the page down β use a fixed overlay or bottom-docked bar instead.
- Dynamically loaded navigation bars that appear after hydration.
- Promotional banners injected by third-party scripts.
- Chat widgets that expand into the content flow rather than floating over it.
If content must arrive dynamically, place it below the fold or use position: fixed / sticky so it overlays existing content instead of displacing it.
Common CLS Mistakes Developers Make
- Lazy-loading above-the-fold images. Never lazy-load your hero or anything in the first viewport. Use
loading="eager"(the default) for above-fold images and reserveloading="lazy"for below-fold media. See the lazy loading guide for where the line sits. - Carousels without a fixed height. Sliders that switch between slides of different heights shift constantly. Lock a fixed height or aspect ratio on the carousel container.
- Client-rendered content with no placeholder. SPA components that populate after hydration shift the page. Use server-side rendering or skeleton placeholders whose dimensions match the final content.
- Ignoring fallback-font metrics. Even with
font-display: swap, a fallback with wildly different metrics still reflows. Size-adjust it (Step 4). - Testing only on fast connections. CLS is worst on throttled connections where resources trickle in over several seconds. Always test with network throttling on.
Tools to Measure and Fix CLS
You can't fix what you can't see. These tools surface exactly which elements move:
- SlapMyWeb Audit β flags every shifting element on the page with the specific node and a copy-paste fix; part of a complete SEO audit.
- Chrome DevTools Performance panel β record a load and read the Layout Shift entries in the Experience lane; each highlights the moved element.
- PageSpeed Insights β shows both lab CLS and field (real-user) CLS; understand what those numbers mean in the PageSpeed score guide.
- Web Vitals Chrome extension β live CLS readout as you click around your own site.
- Lighthouse β ranks the elements contributing most to your CLS.
Lab data tells you what can shift; field data from real users tells you what does. Fix the elements that show up in both first.

CLS Fixes at a Glance
| Cause of shift | Fix | Code property |
|---|---|---|
| Images without size | Set explicit dimensions | width / height, aspect-ratio |
| Late-loading video/embeds | Reserve responsive space | aspect-ratio |
| Async ads | Pre-allocate the slot | min-height + contain: layout |
| Web-font swap reflow | Size-adjusted fallback | font-display: swap, size-adjust |
| Layout-animating UI | Animate off the layout path | transform, opacity |
| Widget internals rippling out | Isolate the box | contain: content |
| Above-fold injection | Overlay, don't displace | position: fixed / sticky |
Once CLS is handled, the same "reserve space, don't block the main thread" discipline carries straight into your other vitals β the technical SEO pillar guide ties stability, speed, and crawlability together.
Frequently Asked Questions
What is a good CLS score?
A good CLS score is 0.1 or below β that's the threshold Google rates as "good" in its Core Web Vitals assessment. Scores between 0.1 and 0.25 need improvement, and anything above 0.25 is classified as poor and can hurt your search rankings, especially on mobile.
Does CLS affect Google rankings?
Yes. CLS is one of three Core Web Vitals Google uses as a ranking signal, introduced in the 2021 Page Experience update. It acts as a tiebreaker: when pages are similarly relevant, the more visually stable one tends to win, and the effect is strongest in mobile search results.
How do I find what's causing CLS on my page?
Open the Chrome DevTools Performance panel, record a page load, and look for Layout Shift events in the Experience lane β each one highlights the element that moved. For a faster path, run a free SlapMyWeb scan, which names the specific shifting elements and hands you the fix code.
Can CLS ever be zero?
Yes, a CLS of 0 is achievable but uncommon. Fully static pages with no late-loading images, ads, web fonts, or injected content can score zero. For most real sites, aim for under 0.05 β that's excellent and leaves headroom for the occasional unavoidable minor shift.
Do user-initiated layout shifts count against CLS?
No. Shifts that occur within 500 milliseconds of a user interaction β like expanding an accordion or clicking "load more" β are treated as expected and excluded from your CLS score. The metric only penalizes movement the user did not trigger.
SlapMyWeb Team
We build SlapMyWeb β a brutally honest AI website audit that scans 240+ SEO, performance and Core Web Vitals signals and hands you the fix code.