The curious case of the high CLS when LayoutShift has no shift?

Kevin McCarthy
3 min readMar 8, 2021

--

TL:DR We had a score of 250 CLS (Cumulative Layout Shift) but all previousRect and currentRect seem to match exactly and couldn’t see any visible shift. Solved by updating our useTransformsetting in react-slick carousel.

We were seeing a LayoutShift of around 0.250 from our carousel image method for measuring outlined here. The way we’d figure out the issue was to look at previousRect and currentRect within the LayoutShift Object. However this time the previousRect and currentRect are identical when I log them out.

Also within devTools Moved from and Moved to are the same.

the LayoutShift Object logged in the console of the chrome DevTools where currentRect and previousRect are the same
A screenshot of the Summary section of a LayoutShift in the Performance tab of Chrome showing the same data for Moved from and Moved to

I also couldn’t see any visible layout shifts even when I record the screen and watch it back frame by frame.

The related node div.slick-track is part of a react component using react-slick carousel which we server side render. I thought that potentially the JS in this was causing it.

Proving JS was the cause

I started playing around in Firefox whose performance tools show very different information to Chrome. I also was able to put debuggers on DOM events rather than lines of code (you may be able to do this in Chrome as well but not sure how).

I could see that JS was running prior to the Paints.

I verified that it was JS that was causing the problem by measuring the page with JS blocked and then measuring the page with JS.
JS blocked — CLS of 0.001

JS allowed — CLS of 0.260

Digging into what JS was causing it

I started deleting files and functions and found when removing the initReactComponents() method it stopped causing the issue. This function call loaded any of our react components on the page client-side

Looking at the HTML changes

From there I dug deeper into what changed in the HTML from what was sent from the server to what ended up on the page after the LayoutShift happened.

FROM SERVER 
<div class="slick-track" style="width:500%;left:-100%">
AFTER LAYOUT SHIFT
<div class="slick-track" style="width: 1905px; opacity: 1; transform: translate3d(-381px, 0px, 0px);" >

So it looks like the width changes from a percentage to a px value. Opacity: 1 is added. And the left:-100% changes to a transform.

It seems this styling change while it didn’t change anything on the page may trick the browser into thinking there was a LayoutShift.

Playing about with react-slick settings and a SOLUTION

The react-slick carousel was based off the slick carousel which had all its settings documented here. By looking through them and taking guesses at anything that seemed relevant I discovered the useTransform option. Setting this to false solved the issue!

We got our CLS back down to well below our target of 100.

FROM SERVER
<div class="slick-track" style="width:500%;left:-100%">
AFTER WITH useTransform:false
<div class="slick-track"
style="width: 1905px; left: -381px; opacity: 1;">

Huzzah!

--

--

Responses (2)