
You have seen it: you go to tap a button, an image loads above it, and everything jumps. You tap the wrong thing. That is layout shift, measured as Cumulative Layout Shift (CLS). This guide explains what causes it and gives you concrete fixes so your content stays put while it loads.
What layout shift actually is
Layout shift happens when a visible element moves from one rendered frame to the next without the user causing it. The browser paints the page, then something arrives late (an image, a font, an ad, an injected banner), and the browser has to re-lay-out the page. Content already on screen gets pushed.
CLS scores this. It roughly multiplies how much of the viewport moved by how far it moved, across the page’s life. A low score means stable. It is one of Google’s Core Web Vitals, so it also affects how your pages are assessed for ranking.
The main causes
Images and video without dimensions
If an <img> has no width and height, the browser does not know how much space to reserve. It reserves zero, paints the text, then the image arrives and shoves everything down. This is the single most common cause.
Ads, embeds, and iframes
Third-party content whose size is unknown in advance behaves the same way. An ad slot that collapses to nothing until an ad fills it will push content when the ad loads.
Late-loading fonts
When a custom font swaps in and it is wider or taller than the fallback, the text reflows. A paragraph that was three lines becomes four, and everything below moves.
Content injected above existing content
A cookie banner, a promo bar, or a lazy-loaded element inserted at the top pushes the whole page down after the reader has already started reading.
How to fix each cause
Always declare dimensions
Set width and height attributes on images and videos. Modern browsers use them to compute an aspect ratio and reserve the correct space before the file loads. In CSS, aspect-ratio does the same for responsive elements.
Reserve space for dynamic content
Give ad slots, embeds, and iframes a fixed minimum height that matches the expected content. If the slot is empty, the space is still held, so nothing jumps when it fills.
Stabilize fonts
Match your fallback font metrics to the custom font using size-adjust, ascent-override, and descent-override. This keeps line counts identical before and after the swap.
Insert banners without pushing content
Reserve space for a cookie or promo bar, or overlay it on top of content rather than inserting it into the flow. Never push existing content down after paint.
A real scenario
A blog had a hero image with no dimensions and a font that swapped late. On mobile, the headline rendered, the image loaded and pushed the headline down, then the font swapped and pushed it again. Readers who tapped the first link often hit the wrong one. Two fixes solved it: adding explicit width and height to the hero image, and matching the fallback font metrics. The visible jump disappeared, and the measured CLS dropped from a poor score to a good one, with no redesign.
Common mistakes and how to fix them
- Only testing on fast connections. Layout shift shows up on slow networks where late assets arrive after paint. Throttle to a slow 3G profile when testing.
- Fixing CLS only above the fold. CLS accumulates over the whole page. Shifts on scroll count too. Check the full page.
- Using CSS transforms as the problem, not the fix. Animating with
transformdoes not trigger layout shift, while animatingtoporheightdoes. Prefer transform for movement. - Forgetting lazy-loaded images. Lazy loading is good, but each lazy image still needs reserved dimensions or it will shift when it enters view.
Action checklist
- Add width and height to every image and video.
- Set aspect-ratio on responsive media.
- Reserve fixed space for ads, iframes, and embeds.
- Match fallback and custom font metrics.
- Overlay banners instead of inserting them into the flow.
- Animate with transform, not layout properties.
- Test with network throttling and scroll the whole page.
Conclusion and next step
Layout stability is mostly about telling the browser how much space things need before they arrive. Your next step: open your most-visited page, throttle your connection, reload, and watch for anything that jumps. Fix the largest jump first, usually an image without dimensions.
FAQ
What CLS score should I aim for?
Google’s published Core Web Vitals threshold treats a CLS of 0.1 or below as good, and above 0.25 as poor. Aim for 0.1 or lower.
Does lazy loading cause layout shift?
Only if the lazy element has no reserved size. Keep lazy loading, but always give the element explicit dimensions or an aspect ratio so its space is held.
Do animations hurt CLS?
Animations that change layout properties like height or top do. Animations using transform and opacity do not count as layout shift, so prefer those.
Why does my CLS look fine in the lab but bad in the field?
Lab tools measure one controlled load. Real users hit slow networks, accept cookie banners, and interact in ways that trigger shifts. Trust field data from real-user monitoring over a single lab run.
References
- web.dev — Cumulative Layout Shift and Core Web Vitals documentation.
- MDN Web Docs — aspect-ratio and image dimension attributes.