How to optimize for CLS when loading more content asynchronously
--
Cumulative Layout Shift (CLS) is an important, user-centric metric for measuring visual stability because it helps quantify how often users experience unexpected layout shifts — a low CLS helps ensure that the page is delightful.
What you might not know is:
- CLS is measured continuously. In fact, its value is also updated while your users scroll your page, if the scroll generates some layout movement
- CLS measurement is paused for 500ms whenever a user interaction like a click or a keyboard event occurs
Long story short, Cumulative Layout Shift measures every unexpected layout movement occurring while your users interact with the page, including while they scroll down.
When loading more content in our pages, the most common source of CLS is the page footer becoming visible for a while, then being pushed below-the-fold again by new, dynamically added content.
So how to keep CLS from growing when we need to load new content dynamically, e.g. to load a whole new page of products? Let’s analyse the two most common techniques currently used to do that:
- Infinite scroll — users will trigger the loading of the new content just by scrolling down towards the bottom of the page
- “Load more” button — users will have to manually trigger the loading of new content by pushing a button. This is generally used when you want your users to be able to reach the footer.
When using infinite scroll
If you use the infinite scroll technique to load additional content onto your web page and you don’t want it to generate CLS, you mustn’t allow the footer to enter the visible portion of the page (the viewport).
You can do so by appending a set of placeholders (or page skeleton) before your users reach the footer, then replacing it with the content as it arrives.
Use the Core Web Vitals extension for Chromium browsers to see how CLS is impacted in the following demos:
- Demo A1 — No skeleton (bad CLS)
- Demo A2 — With Skeleton (good CLS)
Here’s the code of the A2 demo on Codepen.
When using a “Load more” button
If you use a “Load more” button to load additional content onto your web page and you don’t want it to generate CLS, you have two options:
- append new content within 500 ms from the user interaction (the click on the “Load more” button), e.g. by prefetching new content before users actually click
- append a set of placeholders (or page skeleton) as soon as users click, then replace it with new content as soon as it’s done fetching
Use the Core Web Vitals extension for Chromium browsers to see how CLS is impacted in the following demos:
- Demo B1 — No skeleton, content is injected late (bad CLS)
- Demo B2 — No skeleton, content is injected immediately (good CLS)
- Demo B3 — With skeleton, content is injected later (good CLS)
Here’s the code of the B2 and B3 demos on Codepen.
A real-world example
Besides the demos, you can check how we optimized for CLS on Isabel Marant’s product listing page, where new products are retrieved while users scroll down, but they are appended to the page only when users click.
This technique improves user experience by quickly showing the new page, leaves the website accessible to users browsing with assistive technology (AT), and allows users to check the footer, without generating any Cumulative Layout Shifting.
Conclusion
The golden rule to avoid generating CLS is:
Don’t move any visible portion of the page while users scroll down: you’re allowed to do that only after a user interaction, and within 500 milliseconds.
Hope you enjoyed this guide. Add your clap if you did!