<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. Ad sizes increase performance and revenue due to higher click rates and more ads competing in the auction. Unfortunately, this can lead to a suboptimal user experience due to ads pushing visible content you're viewing down the page.
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 how much control you have over the content that will be inserted. If this is inserted by a third-party like an ad partner, you may not know the exact size of content that will be inserted, nor be able to control any layout shifts happening within those embeds.
Reserve space for late-loading content
When placing late-loading content in the content flow, layout shifts can be avoided by reserving the space for them in the initial layout.
One approach is to add a min-height CSS rule to reserve space or—for responsive content such as ads, for example—use the aspect-ratio CSS property in a similar manner to the way browsers automatically use this for images with dimensions provided.
You may 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 on its CLS.
Avoid inserting new content without a user interaction
You've probably experienced layout shifts due to UI that pops-in at the top or bottom of the viewport when you're trying to load a site. Similar to ads, this often happens with banners and forms that shift the rest of the page's content:
If you need to display these types of UI affordances, reserve sufficient space in the viewport for it in advance (for example, using a placeholder or skeleton UI) so that when it loads, it does not cause content in the page to surprisingly shift around. Alternatively, ensure the element is not part of the document flow by overlaying the content where this makes sense. See the Best practices for cookie notices post for more recommendations on these types of components.
In some cases adding content dynamically is an important part of user experience. For example, when loading more products to a list of items or when updating live feed content. There are several ways to avoid unexpected layout shifts in those cases:
Replace the old content with the 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 coming in.
Have the user initiate the load of new content, so they are not surprised by the shift (for example with a "Load more" or "Refresh" button). It's recommended to prefetch the content before the user interaction so that it shows up immediately. As a reminder, layout shifts that occur within 500 milliseconds of user input are not counted towards 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).
Note: If content is likely to take more than 500 milliseconds—for example it requires a network fetch—then reserving the expected space within that 500 millisecond timeframe and taking the impact of any future shift up front ensures any shifts won't be included in the CLS score.
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, incurring a 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 (FOIT—flash of invisible text).
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 shifting of text:
font-display: optional can avoid a re-layout as the web font is only used if it is available by the time of initial layout.
Ensure the appropriate fallback font is used. For example, using font-family: "Google Sans", sans-serif; will ensure the browser's sans-serif fallback font is used while "Google Sans" is loaded. Not specifying a fallback font using just font-family: "Google Sans" will mean the default font is used, which on Chrome is "Times"—a serif font which is a worse match than the default sans-serif font.
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 as detailed in the Improved font fallbacks post.
The Font Loading API can reduce the time it takes to get necessary fonts.
Load critical web fonts as early as possible using <link rel=preload>. A preloaded font will have a higher chance to meet the first paint, in which case there's no layout shifting.
Read Best practices for fonts for other font best practices.
Reduce CLS by ensuring pages are 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 browsers memory for a short period after you navigate away so if you return to them, then they will be restored exactly as you left them. This means the fully loaded page is instantly available—without any shifts which may be normally seen during load due to any of the reasons given earlier.
While this does potentially still mean the initial page load encounters layout shifts, when a user goes back through pages they are not seeing the same layout shifts repeatedly. You should always aim to avoid the shifts even on the initial load, but where that is more tricky to resolve fully, you can at least reduce the impact by avoiding them on any bfcache navigations.
Back and forward navigations are common on many sites. For example, returning to a contents page, or a category page, or search results.
When this was rolled out to Chrome, we saw noticeable improvements in CLS.
The bfcache is used by default by all browsers, but some sites are ineligible for the bfcache due to a variety of reasons. Read the bfcache guide for more details on how to test and identify any issues preventing bfcache usage to ensure you are making full use of this feature to help your overall CLS score for your site.
Conclusion
There are a number of techniques to identify and improve CLS as detailed earlier in this guide. There are allowances built into Core Web Vitals, so even if you cannot eliminate CLS completely, using some of these techniques should allow you to reduce the impact. This will hopefully allow you to stay within those limits, creating a better experience for your website's users.
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2025-02-07 UTC.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-02-07 UTC."],[],[]]