Layout Shift: How to prevent it ? (Cumulative Layout Shit / CLS)


A layout shift occurs when the rendering of the web page shifts / move element from their current position.



For instance:

  • without the knowledge of the target ratio (via the height and width attribute of an image, the browser can't reserve space for the image
  • it download the image calculate the space needed for the image
  • recalculate the layout
  • and shifts element around to be able to put the image

That's what's called a layout shift



  • Set the correct image ratio on the image tag with the height and width attribute


When you change a layout css property after page load (the first rendering), you will also create a layout shift.

Solution: If you want to change two layout property at the same time, you should use the window.requestAnimationFrame function to avoid a layout shift.

Example with a dynamic correction of a fixed top header such as the Bootstrap navbar where:

  • we add the fixed-top class that set the position to fixed
  • and correct it with a padding on the body.
window.requestAnimationFrame(function() {
    let fixedNavbar = document.querySelector(".navbar[data-type=\"fixed-top\"]")
    let offsetHeight = fixedNavbar.offsetHeight;"padding-top",offsetHeight+"px");


Occurences: Devtool

In the devtool, layout shift can be seen

  • in the performance tab:
    • click the start and reload page (Ctrl+Shift+E)
    • stop it when the page has finish loaded
    • check the experience row.

  • in the rendering tool where you can see the shifted region and and the web core vitals.

Cumulative layout shift

The Cumulative layout shift or CLS is a web core vital metrics that groups all layout shift during page load.

  • The devtool can also add an overlay. See the web core vital for more information.

  • A library can report back the cumulative layout shift (or CLS) measurement. See the example below

Calculation Example with Perfume

Example with perfume

  • The element that we are going to shift
<button>Click to see the cumulative value</button>
  • Simulate a layout shift
        let button = document.querySelector("button"); = "block"; "auto"; "auto"; "fit-content";
  • The engine
new Perfume({
  analyticsTracker: options => {
    const { metricName, data, eventProperties, navigatorInformation, vitalsScore, } = options;
    switch (metricName) {
      case 'cls':
        console.log('cumulativeLayoutShift value: '+data);
      case 'clsFinal':
        console.log('cumulativeLayoutShiftFinal: '+data);
  • Output:

Powered by ComboStrap