CLS — What’s the Problem?
I personally don’t like web sites with a lot of layout shift. But the question is how to test it? How to communicate my findings without just saying that the site feels wrong? I’ll briefly talk about Cumulative Layout Shift, a metric from Google’s Web Vitals.
My article is based on the Google article about CLS, on some private workshops I attended, and on my own observations and measurements of one of the projects I test. I’ll show examples throughout this article from that project.
CLS is a user-centric metric, that means it’s not directly related to web performance. It’s related to how users experience a site. It’s an attempt to quantify how users experience layout shifts.
To describe it on a concrete example. Imagine you’re reading an article. You’re half way through a paragraph when suddently the text moves a bit. You’re lost. You have to search for the text you were reading just a moment ago. Is it something you’d enjoy? I should not rely on my assumptions too much here, but I think this is mostly annoying to people.
Calculating layout shift could be shown on the following picture:

The black area gets shifted down because of the red area. After the shift, they take up 80 % of the viewport. This number is called an impact fraction. The red area takes up 30 % of the viewport, that’s a distance fraction. Layout shift is a multiplication of impact and distance frantions. Cumulative layout shift is then a sum of all layout shifts that happen in the viewport.
How should I find out how my web site fares then? There’s a number of tools that could be used for that:
- DevTools in Chrome
- Web Vitals extension for Chrome
- Chrome UX report (CrUX) — if available for your URL or domain
- Lighthouse report
- a custom solution, like the one from the picture that I built using canarytrace.com

One important difference between CrUX report and Lighthouse is that CrUX data come from real users. Google gathers such statistics unless you turn it off somewhere in settings in Chrome. Lighthouse is a robot that measures your site. Web Vitals extention for Chrome combines both your current user experience and CrUX data (if available).
Although it’s probably easier to use the extension or have a look into Lighthouse, I still recommend digging around in DevTools first. By doing that, you’ll gain a better understanding of what’s going on on the site.
Firstly, I’ll navigate to my website, open DevTools (e.g. with pressing F12), go to the Performance tab, and finally run a new performance profile:

I’ll get a lot of data back, which can be a bit overwhelming at first. But for the purpose of finding information about layout shifts, you’ll need just some pieces of information from all this, most importantly the Experience row:

In my concrete example, there are a few red rectangles that point me to moments when layout shift occured. I can find a bit more by looking at the screenshots a few lines above. One of those screenshots looks like this:

and the next one like this:

Here we go! The main banner appeared at this place later, moving the whole layout down. Not good, but I know the culprit now, that’s good.
If I want to visually see how the layout is changed during the page load, I can use some other features in Chrome. I can check Layout Shift Regions in Rendering. It will pain shifted regions blue. It’s also useful to slow down the connection, so the whole experience is slower, therefore more visible to the eye:

Now I hard-reload the site and I can see what regions get shifted.
I can also have a look into Web Vitals extension where I can see a similar story:

It says my CLS was 0.351 and 91 % real users experience similar problems. Web Vitals extension combines current measurement with CrUX data from other real users.
I’ll also have a look into what Lighthouse has to say:

Pretty much the same story. The concrete numbers are not that important, but what I look for are trends and consistency among tools. If I have three different tools and reports telling me there’s a problem, I’m about to start believing there really is.
Next step would be to see the DOM. In that, I can see:
<img class="c-banner__item-image" alt="AKCE TÝDNE - DĚTSKÁ ZIMNÍ OBUV OVONO" data-lazy="https://alpineprooutlet.blob.core.windows.net/apocz/images/banner-174-desktop-1920x450_OVONO_3.jpg" src="/images/loader.gif">
The way this whole thing works is that loader.gif
is displayed first and the banner is lazy loaded later. Not ideal for a viewport.
The problem is much clearer now, but what should I do to solve it?
I recommend experimenting a bit more before reporting this problem. Chrome allows you to set up Overrides. I’ll first remove lazy loading from the picture:
<img class="c-banner__item-image" alt="AKCE TÝDNE - DĚTSKÁ ZIMNÍ OBUV OVONO" src="https://alpineprooutlet.blob.core.windows.net/apocz/images/banner-174-desktop-1920x450_OVONO_3.jpg">

Next time I refresh the page, Chrome will load my local version of index.html
, the one with no lazy loading.
Lighthouse now shows CLS 0.248, Web Vital extension 0.247. It’s still above the max limit that’s considered desired, but it’s less than what it was before.
When I have a look into Performance tab, I no longer see the transition between:

and:

It is an improvement. Unfortunately not the only thing we should fix on the site, there are simply more layout shifts that happen before the page is loaded. Another big problem is that the site originally looks like this:

and only after a while some other content is displayed. This causes layout shift as well, which I can again see in the Performance tab, in Web Vitals extension, in Lighthouse. The process of investigating this problem is the same as in the example of the banner, but I won’t do more of it in this article.
What could be some takeaways from all this?
I see a few things that are worth mentioning more explicitly at the end:
- CLS is a metric related to user experience
- measuring CLS could be tricky across tools, it’s unlikely you’ll get completely same numbers in different tools, or even in one tool over time; it’s more useful to consider ranges and trends
- DevTools are your big friend in investigation, a lot of information could be gathered from there
- you can also attempt to fix the problem in Overrides in Chrome DevTools