Optimize Cumulative Layout Shift

Cumulative Layout Shift (CLS) is one of the three Core Web Vitals metrics. It measures the instability of content by combining how much visible content has shifted in the viewport with the distance the affected elements moved.

Layout shifts can be distracting to users. Imagine you've started reading an article when all of a sudden elements shift around the page, throwing you off and requiring you to find your place again. This is very common on the web, including when reading the news, or trying to click 'Search' or 'Add to Cart' buttons. Such experiences are visually jarring and frustrating. They're often caused when visible elements are forced to move because another element was suddenly added to the page or resized.

To provide a good user experience, sites must have a CLS of 0.1 or less for at least 75% of page visits.

Good CLS values are 0.1 or less, poor values are greater than 0.25, and anything in between needs improvement
Good CLS values are 0.1 or less. Poor values are greater than 0.25.

Unlike the other Core Web Vitals, which are time-based values measured in seconds or milliseconds, the CLS score is a unitless value based on a calculation of how much content is shifting and by how far.

In this guide, we'll cover optimizing common causes of layout shifts.

The most common causes of a poor CLS are the following:

  • Images without dimensions.
  • Ads, embeds, and iframes without dimensions.
  • Dynamically injected content such as ads, embeds, and iframes without dimensions.
  • Web fonts.

Understand the causes of layout shifts

Before you start looking at solutions to common CLS issues, it's important to understand your CLS score and where the shifts are coming from.

CLS in lab tools versus field

Developers often think the CLS measured in the Chrome UX Report (CrUX) is incorrect, because it doesn't match the CLS they measure using Chrome DevTools or other lab tools. Web performance lab tools like Lighthouse might not show a page's full CLS, because they usually do only a simple load of the page to measure some web performance metrics and provide some guidance. However, Lighthouse user flows do allow you to measure beyond the default page load audit).

CrUX is the official dataset of the Web Vitals program. It measures CLS throughout the full life of a page, not just during the initial page load that lab tools typically measure.

Layout shifts are very common during page load, because that's when the system fetches all the resources necessary to render the page, but layout shifts can also happen after the initial load. Many post-load shifts occur as the result of a user interaction and are therefore excluded from the CLS score. As long as they happen within 500 milliseconds of that interaction, they count as expected shifts.

However, unexpected post-load shifts can be included where there's no qualifying interaction—for example, if lazy-loaded content loads fully while the user scrolls through a page. Other common causes of post-load CLS come from interactions between transitions, for example on Single Page Apps, which take longer than the 500-millisecond grace period.

PageSpeed Insights shows both the user-perceived CLS from a URL in its "Discover what your real users are experiencing" section, and the lab-based load CLS in its "Diagnose performance issues" section. Differences between these values are likely the result of post-load CLS.

Screenshot of PageSpeed Insights showing URL-level data highlighting the real user CLS which is considerably larger than the Lighthouse CLS
In this example, CrUX measures a much larger CLS than Lighthouse.

Identify load CLS issues

When the CrUX and Lighthouse CLS scores on PageSpeed Insights are similar, it usually means Lighthouse has detected a load CLS issue. In this case, Lighthouse can provide two audits for more information: one for images causing CLS due to missing width and height, and one for all the elements that shifted during the page load along with their CLS contribution. To see these audits, filter for them as in the following image:

Lighthouse Screenshot showing the CLS audits providing more information to help you identify and address CLS issues
Lighthouse's detailed CLS diagnostics.

The Performance panel in DevTools also highlights layout shifts in the Experience section. The Summary view for a Layout Shift record includes the CLS score as well as a rectangular overlay showing the affected regions. These are especially helpful in identifying load CLS issues because they let you replicate the user experience with a reload performance profile.

Layout Shift records being displayed in the Chrome DevTools performance panel when expanding the Experience section
After recording a new trace in the Performance panel, the Experience section of the results is populated with a red-tinted bar displaying a Layout Shift record. Clicking the record lets you drill down into impacted elements by showing details such as the "moved from" and "moved to" entries in this image.

Identify post-load CLS issues

Disagreement between the CrUX and Lighthouse CLS scores often indicates post-load CLS. These shifts can be tricky to track down without field data. For information on collecting field data, see Measure CLS elements in the field.

The Web Vitals Chrome extension can be used to monitor CLS while you interact with a page, either in a heads-up display, or in the console alongside details about the elements that have shifted.

As an alternative to using the extension, you can browse your web page while recording layout shifts using a Performance Observer pasted into the console.

After you set up shift monitoring, you can try to replicate any post-load CLS issues. CLS often happens while the user scrolls through a page, when lazy-loaded content loads fully without space reserved for it. Content shifting when the user holds the pointer over it is another common post-load CLS cause. Any content shift during either of these interactions counts as unexpected, even if it happens within 500&nbspmilliseconds.

For more information, see Debug layout shifts.

After you've identified any common causes of CLS, you can also use Lighthouse's timespans user flow mode to ensure that layout shifts don't break typical user flows.

Measure CLS elements in the field

Monitoring CLS in the field can be invaluable in determining what circumstances CLS happens in and narrowing down the possible causes. Like most lab tools, field tools measure only the elements that shifted, but that usually provides enough information to identify the cause. You can also use CLS field measurements to determine which issues are the highest priority to fix.

The web-vitals library has attribution functions that let you collect this additional information. For more information, see Debug performance in the field. Other RUM providers have also started collecting and presenting this data similarly.

Common causes of CLS

This section outlines some of the more common reasons for CLS and strategies for avoiding them.

Images without dimensions

Always include width and height size attributes on your images and video elements, or reserve the required space using CSS aspect-ratio. This lets the browser allocate the correct amount of space in the document while the image loads.

Images without width and height specified.

Images with width and height specified.

Lighthouse report showing the before/after impact to Cumulative Layout Shift after setting dimensions on images
Lighthouse 6.0 impact of setting image dimensions on CLS.

width, height, and aspect-ratio

In the early days of the web, developers added width and height attributes to their <img> tags to ensure sufficient space was allocated on the page before the browser started fetching images. This would minimize reflow and re-layout.

<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">

width and height in this example don't include units. These "pixel" dimensions would ensure that the browser reserved a 640x360 area. The image would stretch to fit this space, regardless of whether the true dimensions matched it.

When Responsive Web Design was introduced, developers began use CSS to resize images instead of width and height:

img {
  width: 100%; /* or max-width: 100%; */
  height: auto;
}

However, because the image size isn't specified, space can't be allocated for it until the browser starts to download it and can determine its dimensions. As images load, text shifts down the page to make room for them, creating a confusing and frustrating user experience.

Best practice for setting image dimensions

Because modern browsers set the default aspect ratio of images based on an image's width and height attributes, you can prevent layout shifts by setting those attributes on the image and including the preceding CSS in your style sheet.

<!-- set a 16:9 aspect ratio as 640x360 pixels -->
<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">

The browser then adds a default aspect ratio based on the element's existing width and height attributes, so it can determine how much space to reserve for the image at the start of layout calculations.

Because the major browsers calculate default aspect ratios as the HTML is processed, each browser displays the value a little differently. (For details on why this happens, see Width & height presentational hints.)

For example, Chrome displays aspect ratios like this in the Styles section of the Element panel:

img[Attributes Style] {
  aspect-ratio: auto 640 / 360;
}

Safari behaves similarly, using an HTML Attributes style source. Firefox doesn't display this calculated aspect-ratio at all in its Inspector panel, but does use it for layout.

The auto part of the preceding code is important, because it causes the image dimensions to override the default aspect ratio after the image downloads. If the image dimensions are different, this still causes some layout shift after the image loads, but this ensures the image aspect ratio is still used when it becomes available, in case the HTML is incorrect. Even if the actual aspect ratio is different from the default, it still causes less layout shift than the 0x0 default size of an image with no dimensions provided.

For a fantastic deep dive into aspect ratios and the philosophy of responsive images, see Jank-free page loading with media aspect ratios.

If your image is in a container, you can use CSS to resize the image to the width of the container. We set height: auto; to avoid using a fixed value for the image height.

img {
  height: auto;
  width: 100%;
}

What about responsive images?

When working with responsive images, srcset defines the images you allow the browser to select between and what size each image is. To ensure that <img> width and height attributes can be set, use the same aspect ratio for each image.

<img
  width="1000"
  height="1000"
  src="puppy-1000.jpg"
  srcset="puppy-1000.jpg 1000w, puppy-2000.jpg 2000w, puppy-3000.jpg 3000w"
  alt="Puppy with balloons"
/>

Your images' aspect ratios can also change depending on your art direction. For example, you may want to include a cropped shot of an image for narrow viewports, and display the full image on desktop:

<picture>
  <source media="(max-width: 799px)" srcset="puppy-480w-cropped.jpg" />
  <source media="(min-width: 800px)" srcset="puppy-800w.jpg" />
  <img src="puppy-800w.jpg" alt="Puppy with balloons" />
</picture>

Chrome, Firefox, and Safari now support setting width and height on the source children of the picture element:

<picture>
  <source media="(max-width: 799px)" srcset="puppy-480w-cropped.jpg" width=480 height=400/>
  <source media="(min-width: 800px)" srcset="puppy-800w.jpg" width=800 height=400/>
  <img src="puppy-800w.jpg" alt="Puppy with balloons" width=800 height=400/>
</picture>

Ads, embeds, and other late-loaded content

Images aren't the only type of content that can cause layout shifts. Ads, embeds, iframes, and other dynamically injected content can all cause content appearing after them to shift down, increasing your CLS.

Ads are one of the largest contributors to layout shifts on the web. Ad networks and publishers often support dynamic ad sizes, and larger ads often generate more revenue than smaller ones. However, ads loading in suddenly and pushing visible content down the page is bad for the user experience.

Embeddable widgets allow you to include portable web content on your page, such as videos from YouTube, maps from Google Maps, and social media posts. However, these widgets often aren't aware of how large their contents are before they load. As a result, platforms offering embeds don't always reserve space for their widgets, which causes layout shifts when they finally load.

The techniques for dealing with these are all similar. The major differences are in how much control you have over the inserted content. Third parties like ad partners often won't give you any useful information about inserted content, or any control over the layout shifts these embeds cause.

Reserve space for late-loading content

When placing late-loading content in the content flow, you can avoid layout shifts by reserving space for them in the initial layout.

This can be as simple as adding a min-height styling to reserve space or, for responsive content such as ads, using the new aspect-ratio CSS property in a similar way to browsers using it automatically for images.

Three mobile
    devices with just text content in the first device. This is shifted down on
    the second device, and reserving space with a placeholder as shown on the
    third device prevents the shift.
Reserving space for ads can prevent layout shifts.

You might need to account for subtle differences in ad or placeholder sizes across form factors using media queries.

For content that may not have a fixed height, like ads, you might not be able to reserve the exact amount of space needed to eliminate the layout shift entirely. If a smaller ad is served, a publisher can style a larger container to avoid layout shifts, or choose the most likely size for the ad slot based on historical data. The downside to this approach is that it increases the amount of blank space on the page.

You can instead set the initial size to the smallest size that will be used, and accept some level of shift for larger content. Using min-height, as suggested previously, allows the parent element to grow as necessary while reducing the impact of layout shifts, compared to the 0px default size of an empty element.

Try to avoid collapsing the reserved space by showing a placeholder if, for example, no ad is returned. Removing the space set aside for elements can cause just as much CLS as inserting content.

Place late-loading content lower in the viewport

Dynamically injected content closer to the top of the viewport usually causes greater layout shifts than content injected lower in the viewport. However, injecting content anywhere in the viewport still causes some shift. If you can't reserve space for injected content, we recommend placing it later on the page to reduce the impact.

Overlay banner content

Banners and forms appearing unexpectedly on a page are another common cause of unexpected layout shifts.

Dynamic content without space reserved.

If you can't reserve space for this kind of content, you can instead overlay it on existing content so it's not part of the document flow. For advice on when to use this approach, see Best practices for cookie notices.

Let user interaction trigger expected layout shifts

In some cases, adding content dynamically is an important part of the user experience, for example when loading more products to a list of items or updating live feed content. There are several ways to avoid unexpected layout shifts in those cases:

  • Replace old content with new content within a fixed size container, or use a carousel and remove the old content after the transition. Remember to disable any links and controls until the transition has completed to prevent accidental clicks or taps while the new content is loading.
  • Let the user initiate the loading of new content, for example with a Load more or Refresh Button, so the shift isn't a surprise. We recommend prefetching the new content before the user interaction so it shows up immediately. As a reminder, layout shifts within 500 milliseconds of user input aren't counted toward CLS.
  • Seamlessly load the content offscreen and overlay a notice to the user that it's available (for example, with a Scroll to top button).
Examples of
    dynamic content loading without causing unexpected layout shifts from Twitter and the Chloé website
Examples of dynamic content loading without causing unexpected layout shifts. Left: Live feed content loading on Twitter. Right: "Load More" example on Chloé website. Check out how the YNAP team optimized for CLS when loading more content.

Animations

Changes to CSS property values can require the browser to react to these changes. Some values, such as box-shadow and box-sizing, trigger re-layout, paint, and composite. Changing the top and left properties also cause layout shifts, even when the element being moved is on its own layer. Avoid animating using these properties.

Other CSS properties can be changed without triggering re-layouts. These include using transform animations to translate, scale, rotate, or skew elements.

Composited animations using translate can't impact other elements, and so don't count toward CLS. Non-composited animations also don't cause re-layout. To learn more about which CSS properties trigger layout shifts, see High-performance animations.

Web fonts

Downloading and rendering web fonts is typically handled in one of two ways before the web font is downloaded:

  • The fallback font is swapped with the web font, using Flash of Unstyled Text (FOUT).
  • "Invisible" text is displayed using the fallback font until a web font is available and the text is made visible, using Flash of Invisible Text (FOIT).

Both approaches can cause layout shifts. Even if the text is invisible, it's still laid out using the fallback font, so when the web font loads, the text block and the surrounding content shift in the same way as for the visible font.

The following tools can help you minimize this text shift:

  • font-display: optional causes the web font to be used only if it's available by the time of initial layout.
  • Use fallback fonts similar to the web font. For example, using font-family: "Google Sans", sans-serif; makes the browser use the sans-serif fallback font while "Google Sans" loads. Not specifying a fallback font (using just font-family: "Google Sans") causes Chrome to use its default serif font, which is a worse match.
  • Minimize the size differences between the fallback font and the web font using the new size-adjust, ascent-override, descent-override, and line-gap-override APIs. For details, see Improved font fallbacks.
  • The Font Loading API can reduce the font loading time.
  • Load critical web fonts as early as possible using <link rel=preload>. A preloaded font is more likely to be ready for the first paint and cause no layout shift.

For more information, see Best practices for fonts.

Make your pages eligible for the bfcache

A highly effective technique for keeping CLS scores low is to ensure your web pages are eligible for the back/forward cache (bfcache).

The bfcache keeps pages in the browser's memory for a short time after you navigate away, so that if you return to them, they're restored exactly as you left them. This means the fully loaded page is instantly available, without any shifts that might happen while the page loads.

While this can still mean the initial page load has layout shifts, it allows users to avoid seeing the same layout shifts repeatedly. This reduces the impact of layout shifts you might have had trouble resolving in other ways.

Back and forward navigations are common on many sites, such as when returning to a contents page, or a category page, or search results.

The bfcache is used by default by all browsers, but some sites are ineligible for it for a variety of reasons. See the bfcache article for more details on how to test and identify any issues preventing bfcache usage.