How To Fix Cumulative Layout Shift (CLS)

The ultimate guide to fixing the Cumulative Layout Shift for your site
Arjen Karel Core Web Vitals Consultant
Arjen Karel
linkedin

How To Fix Cumulative Layout Shift (CLS) - in short

Cumulative Layout Shift (CLS) is an important, user-centric metric for measuring visual stability.

Cumulative Layout Shift (CLS) measures the movements of the page as content renders or new content is shown on the page.

It calculates a score based on how much of the page is unexpectedly moving about, and how often. These shifts of content are very annoying, making you lose your place in an article you’ve started reading or, worse still, making you click on the wrong button!

How To Fix Cumulative Layout Shift (CLS)

What is Cumulative Layout Shift (CLS)?

Cumulative Layout Shift (CLS) is an important, user-centric metric for measuring visual stability. Cumulative Layout Shift (CLS) measures the movements of the page as content renders or new content is shown on the page. It calculates a score based on how much of the page is unexpectedly moving about, and how often. These shifts of content are very annoying, making you lose your place in an article you’ve started reading or, worse still, making you click on the wrong button!.

How is CLS Calculated?

The CLS is easily calculated using a simple, yet elegant formula:

CLS = sum(impact-fraction * distance-fraction)

The impact fraction measures how much of the visible content of the page has shifted. The distance faction measures how far the content has shifted. If, for example 50% of the page (how much) has shifted 25% (how far) of the viewport of the page. The CLS score = .50 * .25 = 0.125

Expected VS unexpected layout shifts

Not all layout shifts are bad, only the ones that you do not expect. When you clicks on a 'load more items' link for example you would expect more items to appear on the page and the the content of the page to shift.

That is why only unexpected layout shifts will cause a CLS contribution. For, example, if a user clicks a button and in response new content will be added to the page (for example a menu dropdown) the layout shift will be excluded from the CLS. To be precise:
Layout shifts that occur within 500 milliseconds of user input will be excluded from calculations. 

Layout shift sessions

When CLS was first introduced some sites were unfairly punished with a bad CLS score. For example a page that implements infinite scrolling would have gotten the impact of the newly added content added to the total CLS score for each new scroll. That is why the CLS is now calculated in sessions. Each sessions has a 5 second duration. The session with the largest CLS score will become the final CLS score.
If, for example, during the first 5 seconds the CLS score is 0.095, then in the next 5 second sessions the CLS is 0.15 and for the last session the CLS score is 0. The final CLS score will be the highest of the three 0.15.

A quick reminder: CLS and the Core Web Vitals!

Cumulative Layout Shift (CLS) is an important, user-centric metric for measuring visual stability. The Cumulative Layout Shift (CLS) is part of the Core Web Vitals along with the First Contentful Paint, the Largest Contentful Paint and the First Input Delay.

How to find CLS issues

1. Use Lighthouse to find Cumulative Layout Shifts

The easiest method to find layout shifts is by using Lighthouse in your own chrome browser. Simply run a Lighthouse audit by right clinking anywhere on the page. Then select inspect-element, select the lighthouse tab in the now-opened console and run the audit. 

The audit results will show the Cumulative Layout Shift (CLS) score. Scroll down to Diagnostics and expand the Cumulative Layout Shift information to see what nodes are causing the layout shift.

To be honest I never really use this method myself. The lack of details on the exact distance of the layout shift make these results harder to interpret. 

2. Use the CLS Visualizer to find Cumulative Layout Shifts

The CLS Visualizer is a chrome extension written by me. With a single button click all the layout shifts on the page are visualized. This is the first solution I go to when trying to determine a layout shift on a page. It's easy and will give a great visual overview of the Cumulative Layout Shift.

3. Use the Chrome Performance tab to find CLS

By far the best way to debug a layout shift is though the chrome performance tab.  To open the performance tab navigate to any page in chrome and use this shortcut combination:

- Press CTR-Shift-I (Open Dev Tools)
- Press Ctrl-Shift-E (Start profiling and reload page)

Now inspect the timeline frame by frame by hovering your mouse over the timeline on the top and look for layout shifts (layout shifts are also colored red in the Experience section of the Performance tab). Difficult? Just watch the explainer video below:


Fix CLS caused By images

Images are the usual suspect when it comes to Cumulative Layout Shift. I find that over 40% of all CLS issues are caused by images.

Layout shifts caused by images are in 100% of the cases due to the fact that the image dimensions of the image are not set. Not in the HTML <img> tag and not in the Stylesheets. The fix is easy. Just make sure the image renders with the correct dimensions.

<img 
  src="img.jpg" 
  alt="this image will cause a layout shift"
>
<img src="img.jpg" widht="200" height="200" alt="this image will not cause a layout shift" >

When setting the height and width of an image it is usually a good idea to constrain the maximum width of the image to prevent the image from appearing larger then the mobile screen. Add this to your stylesheet to prevent 

<style>
img{
  max-width:100%;
  height:auto
}
</style>

Alternatively you could also set the width and height of the image in your stylesheet

<style>
img#my-image{
  width:200px;
  height:200px;
}
</style>
<img 
  id="my-image" 
  src="img.jpg" 
   alt="this image will not cause a layout shift"
>

This solution as as good in preventing layout shifts as the previous solution but it does not consider responsive images. With larger images you will need to use media queries to ensure the image does not have larger dimensions then the screen dimensions.

>CLS caused by image before
CLS caused by image after

Fix CLS caused by webfonts

A cumulative lay out shift can be caused by webfonts. WebFonts are are fonts that are not installed on your local computer or phone but downloaded from the internet. While the webfont is not yet downloaded the page is usually rendered with a fallback font.  The size of this fallback font may differ from the final font. In this example the fallback font is slightly smaller then the final webfont.

CLS caused by image before
CLS caused by image after

It is quite simple to fix this behaviour. We have to make sure the fallback font matches the final font more closely. One way to do this is by using the Font with a class method. Basically a class is added to the <html> node of the page once the final webfont has been downloaded. With CSS you can then modify the way the font behaves in both states.

<link 
  rel="preload" 
   href="/some-font.woff2" 
   as="font" 
  onload="document.documentElement.classList.add("fl");"
>
h1{
   font-family: 'Meriweather', serif;
   letter-spacing: 2px;
}

html.fl h1{
   letter-spacing: initial;
}

Fix CLS caused by icons

One of the smaller but easy-to-fix layout shifts originate from icons fonts like font awseome or icomoon. On first render the icon font file has not been downloaded yet. The font is rendered at 0x0 pixels. Once the font has been downloaded the icon is painted on the screen with it's final dimensions.
CLS caused by image before
CLS caused by image after

The fix for this layout shift is easy. Use CSS to reserve the space needed for the final icon

nav .searchicon{
width:64px;
height:64px
}

Fix CLS issues caused by user interactions

In the example below the load more button clearly triggers a layout shift. However the layout shift will not be added to the CLS metrics. This is because this layout shift is intentional.  

A browser will know this because the now visible elements have an attribute called ' hadRecentInput'. When this is set to true AND the layout shift happens withing 500ms of the input event (the button click) the layout shift will not be reported by the browser.

Ensure User Interactions Complete Within 500ms

Fix CLS issues caused by AJAX

AJAX allows web pages to be updated asynchronously by exchanging data with a web server behind the scenes. Obviously injecting new content into any webpage could lead to a massive layout shift. That is why it is wise to reserve the space that is used for the new content. Obviously you do not know the height of the new content in advance but what you can do is take an educated guess.

For example, if the average ajax content takes up 50% of the screen it is wise to reserve that 50%. When the new content end up taking up 40 or 60& of the screen the CLS (50% - 40% = 10% distance-fraction) is still a lot smaller then 50% distance-fraction.

Here is an example on how to do this:

<style>
#ajax{height:400px}
#ajax.loaded{height:auto;}
</style>
<script>
fetch('/ajax-content)
   .then(response => response.json())
   .then(result => {
     let el = document.getElementById('ajax');
     el.innerHTML(result.html);
     el.classList.add('loaded');
   })
<script>


Fix CLS issues caused by ads

Ads will often load (a lot) later on the page. This makes Cumulative Layout Shifts caused by ads especially annoying.  When multiple ads are loading in the visible viewport the 'page just wont stay still'. To fix this also reserve the space for the ads, especially the ads in the visible viewport.

<style>
// rectangle mobile ad
.ad{min-height:250px;background:#dedede}
// banner desktop ad
@media only screen and (min-width: 600px) {
.ad{min-height:90px}
}
</style>
lighthouse 100 score

Let's talk about PageSpeed

Tell me about your pagespeed goals. I am a Core Web Vitals consultant and I help speed up websites.

How To Fix Cumulative Layout Shift (CLS)Core Web Vitals How To Fix Cumulative Layout Shift (CLS)