How to Fix Cumulative Layout Shift (CLS) on Any Website

High CLS scores hurt your Google rankings and frustrate users. Here's how to diagnose and fix every major source of layout shift on any website.
Why Your Page Keeps Jumping (and Why Google Cares)
You know the feeling. A page loads, you go to tap a button, and suddenly everything shifts. The button you were about to press is now three inches lower, and you've just accidentally opened an advert or navigated somewhere you didn't intend. It's irritating, and it happens because of something called Cumulative Layout Shift — CLS for short.
CLS is one of Google's three Core Web Vitals, the metrics that directly influence your search rankings. A high CLS score tells Google your site is unstable and frustrating to use, which translates into ranking penalties and higher bounce rates. According to Google's research, pages with good Core Web Vitals scores see 24% fewer session abandonment rates compared to pages that fail.
For hospitality businesses, retail sites, and anyone relying on their website to convert visitors into bookings or purchases, layout shift is a silent conversion killer. We've audited hotel websites with CLS scores above 0.5 — that's catastrophically bad — and each time, the causes are consistent and fixable.
Here's how to diagnose and fix every major source of layout shift on any website.
Understanding the CLS Score
Before fixing anything, it helps to know what you're measuring. CLS is calculated by multiplying two numbers:
Impact fraction × Distance fraction = Individual shift score
The impact fraction is the proportion of the viewport affected by the shift. The distance fraction is how far the element moved as a proportion of the viewport. A large image that slides down 20% of the screen height will have a much higher score than a small label that shifts by a pixel.
Google's scoring thresholds: - 0–0.1: Good - 0.1–0.25: Needs improvement - 0.25+: Poor
Anything above 0.1 will start to impact your search rankings and frustrate your users. Most sites we audit are in the 0.1–0.4 range — genuinely problematic, but entirely fixable.
How to Measure Your Current Score
The fastest way to check is Google PageSpeed Insights, which gives you both lab and field data. For development work, the Layout Shift tab in Chrome DevTools' Performance panel lets you see exactly which elements are shifting and why.
Lighthouse in Chrome DevTools will also flag CLS issues during a local audit. For continuous monitoring, Google Search Console's Core Web Vitals report shows real-user data aggregated over 28 days — this is the number that matters for rankings.
The Six Most Common Causes of Layout Shift
1. Images Without Explicit Dimensions
This is the single most common cause of layout shift, and it's also the simplest to fix.
When a browser encounters an <img> tag without explicit width and height attributes, it reserves no space for the image. The page renders, text flows around where the image will eventually be, and then — as the image loads — everything shifts to accommodate it.
The fix is to always specify both width and height on every image:
<img src="/restaurant-interior.jpg" width="800" height="600" alt="Restaurant interior" />In Next.js, the <Image> component handles this automatically when you provide width and height props. If you're using the fill prop instead, wrap the image in a container with an explicit aspect-ratio CSS rule:
<div style={{ position: 'relative', aspectRatio: '4/3' }}>
<Image src="/interior.jpg" fill alt="Restaurant interior" />
</div>We fixed a hotel website last year that had a CLS score of 0.38 — almost entirely caused by unsized hero images. Adding explicit dimensions to five images brought the score down to 0.04. Nothing else changed.
2. Dynamically Injected Content
Content that loads after the initial render — adverts, cookie banners, chat widgets, promotional bars — is a major source of layout shift if it appears above existing content and pushes everything down.
The solutions:
- Reserve space in advance using min-height on the container where dynamic content will appear. If your cookie banner is always 60px tall, give its container min-height: 60px so the space is already accounted for before it renders.
- Load above-the-fold dynamic content at the top so it occupies reserved space from the start.
- **Use position: fixed or position: sticky** for elements like cookie banners that shouldn't displace page content at all.
Cookie banners are particularly problematic. A banner that slides in from the bottom and overlaps content is fine for CLS; a banner that pushes the entire page body down 100px is not. Make the right architectural choice when implementing them.
3. Web Fonts Causing Text Swaps (FOIT/FOUT)
When a custom font loads, the browser may initially render text in the fallback system font, then swap to the custom font once it's downloaded. This swap can cause layout shift if the two fonts have different metrics — different character widths, heights, or line heights.
The most effective fix is using font-display: optional for fonts that aren't critical, and the newer size-adjust, ascent-override, and descent-override CSS properties to make your fallback font metrics match your custom font exactly.
For Google Fonts, font-display: swap is the default. But switching to font-display: optional means the font only loads if it's already in cache — meaning the first visit may not use the custom font, but subsequent visits (and crawler visits during PageSpeed testing) won't experience a swap at all.
In Next.js 13+, the built-in next/font module handles font loading optimally, including injecting size-adjust metrics automatically. If you're on an older setup still loading Google Fonts via a <link> tag, migrating to next/font is one of the most impactful CLS improvements available for Next.js sites.
4. Late-Loading Third-Party Embeds
Embedded content — YouTube videos, Google Maps iframes, social media posts, booking widgets — frequently causes layout shift because the embed library injects an iframe of unknown size after the page has loaded.
The fix is to always provide an explicit aspect ratio wrapper:
.video-wrapper {
position: relative;
aspect-ratio: 16 / 9;
}
.video-wrapper iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}For booking widgets — a common culprit on hospitality sites — check whether the provider offers a minimum height parameter. If not, apply a min-height to the wrapper before the widget loads.
5. Animations That Move Layout-Affecting Properties
Any CSS animation that changes width, height, top, left, margin, or padding will trigger layout shift. These properties cause the browser to recalculate the layout of surrounding elements.
GPU-accelerated properties — transform and opacity — do not affect layout and are safe to animate freely:
/* BAD — causes CLS */
.card:hover {
margin-top: -4px;
}
/* GOOD — no CLS */
.card:hover {
transform: translateY(-4px);
}This is a common pattern in CSS written without CLS awareness. An audit of your stylesheet for transition declarations using layout-affecting properties will often reveal several quick wins.
6. Skeleton Loaders That Aren't the Right Size
Skeleton loaders are meant to reduce the perception of loading time by showing a placeholder in the shape of the actual content. But if the skeleton's dimensions differ from the real content — even by a few pixels — there will be a shift when the real content appears.
The skeleton must match the eventual content dimensions exactly. Measure the actual rendered height of your content components and match the skeleton precisely. If the content is dynamic (e.g. product descriptions of varying length), use a fixed minimum height and allow the content to push past it rather than snapping to a different height.
Key Takeaways: Your CLS Fix Checklist
Use this as a rapid audit checklist for any website:
- All
<img>tags have explicitwidthandheightattributes - Next.js
<Image>components use eitherwidth/heightor anaspect-ratiocontainer withfill - Cookie banners and notifications use
position: fixedor have pre-reserved space - Custom fonts use
next/font(Next.js) orfont-display: optionalwith size-adjust overrides - Embedded iframes (video, maps, booking widgets) are wrapped in
aspect-ratiocontainers - CSS animations use
transformandopacity, not layout-affecting properties - Skeleton loaders match actual content dimensions exactly
Running through this list takes about 30 minutes on a typical site and routinely drops CLS scores from "Poor" into "Good" territory.
How We Fix CLS at LogicLeap
CLS optimisation is part of every website build we deliver. We run Lighthouse audits throughout development — not just at the end — so layout shift issues are caught when they're created rather than patched after launch.
For one restaurant client whose CLS was 0.31 on mobile — largely due to late-loading Instagram embeds and an unsized hero carousel — we brought the score to 0.06 in a single afternoon of targeted fixes. The improvement showed up in Google Search Console's Core Web Vitals report within three weeks, and organic traffic to their menu page increased by 18% in the following month.
If your site is struggling with CLS or Core Web Vitals more broadly, we can perform a targeted performance audit and fix the issues properly — not just paper over them. Get in touch and we'll tell you exactly what's causing your score to suffer and how to resolve it.
Need help implementing this?
We build high-performance websites and automate workflows for ambitious brands. Let's talk about how we can help your business grow.
More Articles

Vercel Edge Functions: When They're Actually Worth It
Edge functions promise lightning-fast responses from servers near your users. Here's exactly when they pay off — and when they'll cause you headaches.

Server-Side Rendering vs Static Generation in Next.js: When to Use Each
SSR or SSG? The wrong choice costs you speed or accuracy. Here's a practical framework for making this critical Next.js architectural decision correctly.