Fix Cumulative Layout Shift (CLS): Stop Your Page From Jumping
Complete guide to diagnosing and fixing Cumulative Layout Shift. Learn why elements jump around and how to achieve a CLS score under 0.1.
Cumulative Layout Shift (CLS) measures how much your page content jumps around while loading. Nothing frustrates users more than clicking a button only to have an ad load and push it out of the way.
Why CLS Matters
- User frustration — users misclick when elements shift
- Lost conversions — accidental clicks on wrong elements, or users abandoning the page
- SEO ranking signal — Google uses CLS as one of three Core Web Vitals
| CLS Score | Rating | Experience |
|---|---|---|
| ≤ 0.1 | Good | Visually stable |
| 0.1–0.25 | Needs Improvement | Occasional shifts noticed |
| > 0.25 | Poor | Content jumps significantly |
Common Causes & Fixes
1. Images Without Dimensions
When an image loads without width and height, the browser doesn't know how much space to reserve.
<!-- ❌ BAD — no dimensions, causes layout shift -->
<img src="photo.webp" alt="Product" />
<!-- ✅ GOOD — browser reserves exact space -->
<img src="photo.webp" alt="Product" width="800" height="600" />
For responsive images, use CSS aspect-ratio:
img {
width: 100%;
height: auto;
aspect-ratio: 4 / 3;
}
2. Web Fonts Causing Text Shifts (FOUT)
When a custom font loads, text resizes and shifts layout.
/* ✅ Use font-display: optional or swap with size-adjust */
@font-face {
font-family: "MyFont";
src: url("/fonts/myfont.woff2") format("woff2");
font-display: optional; /* prevents layout shift entirely */
}
Alternatively, preload your font:
<link rel="preload" as="font" href="/fonts/myfont.woff2" type="font/woff2" crossorigin />
3. Dynamic Content Injection
Ads, cookie banners, and notification bars inserted above content push everything down.
Fix: Reserve space with a fixed-height container:
.ad-slot {
min-height: 250px; /* reserve space for the ad */
background: #f0f0f0;
}
For cookie banners, use a fixed/sticky position so they overlay rather than push content.
4. Late-Loading Third-Party Embeds
YouTube embeds, social widgets, and chat widgets cause shifts when they load.
Fix: Use <iframe> with explicit dimensions or a placeholder container:
<div style="aspect-ratio: 16/9; background: #000;">
<iframe
width="100%"
height="100%"
src="https://www.youtube.com/embed/..."
loading="lazy"
title="Video"
></iframe>
</div>
5. CSS Animations Triggering Layout
Animations that change height, width, top, left, or margin cause layout shifts.
/* ❌ BAD — triggers layout shift */
.dropdown { height: 0; transition: height 0.3s; }
.dropdown.open { height: 200px; }
/* ✅ GOOD — use transform instead */
.dropdown { transform: scaleY(0); transition: transform 0.3s; transform-origin: top; }
.dropdown.open { transform: scaleY(1); }
How to Measure CLS
Chrome DevTools
- Open Performance panel
- Record a page load
- Look for Layout Shifts in the Experience track
- Click each shift to see which elements moved
Web Vitals Extension
Install the Web Vitals Chrome extension to see CLS in real-time as you browse.
Field Data
Use web-vitals library to measure real user CLS:
import { onCLS } from "web-vitals";
onCLS(({ value }) => console.log("CLS:", value));
Quick Wins Checklist
- Add
widthandheightto all images and videos - Use
aspect-ratiofor responsive containers - Set
font-display: optionalor preload fonts - Reserve space for ads, banners, and embeds
- Avoid inserting content above existing visible content
- Use
transformanimations instead of layout properties
Ready to stop wasting ad spend?
Track your landing page performance, monitor Core Web Vitals, and calculate exactly how much slow pages cost you.
Start Free — No Credit Card