How to fix Cumulative Layout Shift
Why pages jump around as they load, and how to reserve space in advance so they stop doing it.
Cumulative Layout Shift measures something specific: how much visible content moves around without a visitor doing anything to cause it. An image pops in and pushes a paragraph downward after you had started reading it. A banner loads in above a button just as you were about to tap it, and you tap the wrong thing instead. Google's published threshold for a good CLS score is under 0.1.
Unlike the other Core Web Vitals, CLS has almost nothing to do with server speed. It is decided entirely by whether the page tells the browser, in advance, how much space something is going to take up. Get that right and content stays still no matter how fast or slow the connection is; get it wrong and even a fast-loading page will visibly rearrange itself.
The one idea behind every fix in this guide
Every cause of layout shift comes down to the same thing: something arrived after the browser had already laid out the page around a different amount of space. The fix is always to reserve the correct space upfront, before the content that will fill it has arrived, so there is nothing left to shift when it does.
Images and video without dimensions
This is the most common cause by far. If an <img> tag has no width and height attributes, the browser has no way to know how tall the image will be until it has actually downloaded it — so it renders everything below the image first, then shoves it all downward once the image arrives and its real dimensions are known.
<img src="photo.jpg" width="800" height="450" alt="...">
Setting both attributes tells the browser the image's aspect ratio immediately, so it reserves the correct box before a single byte of the image itself has loaded. This costs nothing in terms of load time — it only affects layout — which makes it one of the highest-value, lowest-effort fixes available. It applies to embedded video and iframes exactly the same way.
If width and height are set, browsers calculate the correct aspect ratio and reserve space even before any CSS has been applied. Skipping these attributes and relying on CSS alone to size images still leaves a gap during the earliest part of the page load.
Web fonts that swap after the page renders
If a page uses a custom web font, text is often rendered first in a fallback system font while the real font downloads, then swapped once it arrives. Because different fonts have different letter widths, that swap can reflow entire blocks of text — headlines wrap differently, paragraphs push content below them up or down.
How to stop web fonts slowing your page covers this properly, including preloading the font file so it arrives sooner and choosing a fallback font with similar letter widths so the swap, when it happens, moves as little as possible.
Ads, embeds and anything injected by script
Advertising slots, social media embeds and any content injected by third-party JavaScript are a frequent cause of shift because the space they need is often unknown until the script has run. Reserve space for these in advance with a fixed-size or min-height container, so the surrounding content does not need to move once the embed actually loads in.
A cookie banner, a promotional bar, or a "sign up" prompt that slides in above the main content after the page has already rendered is one of the most visible forms of layout shift there is — it moves everything a visitor was already looking at. If something has to appear, reserve its space in the initial layout rather than inserting it afterwards.
Animations that use the wrong CSS properties
Animating width, height, top or left forces the browser to recalculate the layout of everything around the animated element on every frame, and each of those recalculations counts as a shift. Use transform and opacity instead — they are handled by the browser's compositor without affecting the layout of anything else on the page, which is both smoother and does not register as CLS at all.
How to find which element is actually causing it
A CLS score on its own only tells you a problem exists, not what it is. Most speed testing tools that report Core Web Vitals will list the specific elements involved in each shift, usually with a "before" and "after" position. Check this before making changes — it is far faster than guessing which of several possible causes applies to your particular page, and reading a waterfall chart alongside it will show you the order things actually loaded in.
A working checklist
-
Add width and height to every image and video
Including any set as CSS background images where a fixed aspect ratio can be given instead.
-
Preload the main web font and set a close fallback
Reduces both how long text is invisible for and how far it moves when the real font swaps in.
-
Reserve space for ads and embeds with min-height containers
Size the box before the script that fills it has run, not after.
-
Never inject banners or prompts above existing content
If it must appear, give it space in the initial layout instead of sliding it in afterwards.
-
Animate with transform and opacity only
Avoid animating properties that change an element's size or position in the normal flow of the page.
None of this depends on hosting, plan size, or server hardware — a page with these fixes in place holds still on the slowest available connection just as reliably as on the fastest. Once layout is stable, improving Largest Contentful Paint is the next Core Web Vital worth working through, since the two are often diagnosed from the same test result.
Related reading
What LCP, CLS and INP actually measure, why Google picked these three, and the published thresholds for each.
How to stop web fonts slowing your pageHow a web font ends up delaying the page or shifting its layout, and the specific settings that fix each cause.
How to lazy-load imagesHow native lazy-loading works, and the one image on every page that should never use it.
How to compress images for the webHow to get images down to a sensible size for the web without the loss of quality being visible.