---
title: 'Prevent layout shift when toggling scrollbar on Window'
date: '2021-08-16'
lastmod: '2024-11-07'
tags: ['css', 'tricks', 'layout-shift', 'scrollbar', 'window']
summary: 'When toggling the scrollbar on the Window, the layout shifts. This is because the scrollbar is added to the Window. How to prevent this with CSS?'
images: ['/static/images/banners/macbook-pro-on-brown-wooden-table__vinayak_125__AWSHg2mK-Ho.jpeg']
authors: ['default']
---

When will the scrollbar toggle? For example:

- Open a `Modal`, `Popup`, or `Drawer` with a lot of content and prevent the user from scrolling the `Window`.
- Toggle an `Accordion` or a `Tab` which expands the document height that leads to shows up the scrollbar.
- ...

On **Window** devices, these cases will cause the layout shift. This is because the scrollbar is added to the `Window` which makes the `Window`'s width smaller.
This experience is not good for the user.

So let's prevent this layout shift with CSS.

## The classic fix

```css:global.css showLineNumbers
html {
  overflow-y: scroll;
}
```

The classic fix is to add the `overflow-y: scroll` to the `html` element. This will always show the scrollbar on the `Window` (even if the content's height is smaller than the viewport's height).

The scrollbar in this case will always be visible. And its background turns to gray (if the `Window` isn't scrollable).

And of course, I don't recommend this solution <Twemoji emoji="grinning-face-with-sweat" /> cause I think it looks bad as the original problem.

## Using view width

A better solution is to add an invisible `margin-left` to the `html` element with the same width as the scrollbar.
This `margin-left` will be visible when the scrollbar is added to the `Window` and invisible when the scrollbar is removed from the `Window`.

But how to get the scrollbar's width <Twemoji emoji="thinking-face" />?

The answer is to use the `vw` unit.

If you don't know yet, `vw` or [view width](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#relative_length_units) is a **CSS** unit just like `px`, `%`, or `rem` but it's relative to the viewport's width.
`100vw` is equivalent to `100%` of the viewport's width (including the scrollbar), and `100%` width (for the `html` element) is equivalent to 100% of the viewport's width (without the scrollbar) <Twemoji emoji="exploding-head" />

So, we can calculate the width of the scrollbar by subtracting the `100%` width from the `100vw` width with the CSS's `calc()` function like this:

```css:global.css showLineNumbers
html {
  margin-left: calc(100vw - 100%);
}
```

The `margin-left` value will equal `0` when the `Window` isn't scrollable and equal to the scrollbar's width when the `Window` is scrollable.

> [!NOTE]
> If you use this way to prevent layout shift and your application has a `fixed`'s element (like a **Popup** or a **Modal**) which prevents Window scroll with the `overflow-y: hidden` style when it's open,
> then you should also add the `margin-left` to the `fixed` element as well.

For example:

```css:global.css showLineNumbers
html,
.popup-overlay,
.modal-overlay {
  margin-left: calc(100vw - 100%);
}
```

Another solution is to set the width of the `html` element to `100vw` and prevent horizontal scroll:

```css:global.css showLineNumbers
html {
  width: 100vw;
  overflow-x: hidden;
}
```

This solution only makes sense if your application doesn't have to scroll horizontally (like a blog or a documentation website).
And I'm using this in my blog, you can open the `devtool` and check the `html` styles to see it <Twemoji emoji="beaming-face-with-smiling-eyes" />.

## The no-code way <Twemoji emoji="star-struck" size="base" />

There are 2 ways to prevent layout shift without writing any CSS code:

- The first one is to tell your users not to use **Window** device and to use a **Mac** or a **Linux** device instead <Twemoji emoji="grinning-squinting-face" />

- The second one is to just... leave it as it is, sometimes it's not a big deal, and your user doesn't actually care about that <Twemoji emoji="man-shrugging" />

If you have any other solutions, please let me know in the comments below.

Happy styling <Twemoji emoji="clinking-beer-mugs" />
