How to Optimize Cumulative Layout Shift (CLS)

A quick overview of what is Cumulative Layout Shift (CLS), what factors affect it, and how to improve it

In our previous article, we discussed Core Web Vitals. In this article, we will focus on the Cumulative Layout Shift (CLS) and how to optimize it.

Let’s get started!

Quick Overview

The CLS measures the visual stability of a page’s layout.

What is a layout shift?

Imagine that an image loads slower than the text of a page. As a result, the first paragraph is displayed first. A few moments later, when the image loads, it pushes down that paragraph.

That is called a layout shift!

Expected vs. unexpected layout shifts

Some of the layout shifts are expected. For example, when a user performs an action (like clicking a button), a layout shift may be expected and if it happens within the next 500ms, it won’t count for CLS.

On the other hand, if an ad pops out of nowhere, pushing 50% of the visible content out of the viewport, that would add 0.5 to CLS (which is horrible for user experience).

What does “Cumulative” stand for?

Multiple unwanted layout shifts can occur while a user is on a web page. These are measured in a cumulative way per session. In other words, the sum of these layout shifts in a given session. Here is an article that explains in detail how CLS is calculated.

What can cause a poor CLS?

The major causes for a poor CLS are:

  • images that have no explicit dimensions set (width and height)

  • ads, iframes, and embeds that have no explicit dimensions set (width and height)

  • web fonts loading after the unstyled text has been rendered

Steps to Optimize CLS

Now that we know what can cause poor CLS, we can discuss what we can do to improve it!

The steps are pretty simple:

  • Always set explicitly width and height attributes on images and video elements. (Bonus tip: use very low-resolution versions of the original images as placeholders until the original images are downloaded).

  • Maintain the same aspect ratio for all image sizes.

  • Reserve enough space for content that’s injected dynamically (ads, banners, promos, etc.).

  • Alternatively, add the new content below the existing content, unless it’s a response to a user’s action. Layout shifts outside the visible viewport don’t count for CLS.

  • The simplest solution is to not use a font unless it’s absolutely necessary.

  • Preload the fonts that you definitely want to use, like this:

<head>
  <!-- Put this as close to the top as possible -->
  <link rel="preload" href="/MyFont.woff" ... />

  <!-- Other links follow -->
</head>
  • Alternatively, you can mark a font as optional — if the font takes more than 3 seconds to load, the browser stops trying to load it:

@font-face {
  font-family: MyFont;
  src: url('/MyFont.woff') format(woff2);
  [...]
  font-display: optional;
}
  • Bonus —  use the Font Style Matcher tool to minimize the layout shift when your font loads and is applied to the initial unstyled rendered text.

Conclusions

In this article, we studied how to optimize the Cumulative Layout Shift (LCS).

We discussed what causes layout shifts, how they are measured cumulatively, and how to improve CLS.

In our next article, we will talk about optimizing Interaction to Next Paint (INP). That’s a new metric that replaced the First Input Delay (FID). Don’t miss it!

Thank you for reading!