performance

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

7 min
performanceseoweb developmentnext.js

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

March 25, 20267 min read
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:

html
<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:

jsx
<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:

css
.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:

css
/* 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 explicit width and height attributes
  • Next.js <Image> components use either width/height or an aspect-ratio container with fill
  • Cookie banners and notifications use position: fixed or have pre-reserved space
  • Custom fonts use next/font (Next.js) or font-display: optional with size-adjust overrides
  • Embedded iframes (video, maps, booking widgets) are wrapped in aspect-ratio containers
  • CSS animations use transform and opacity, 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.