Stop Scrollbar Layout Shifts With scrollbar-gutter
CSS scrollbar-gutter: stable can reserve scrollbar space before a classic scrollbar is needed. That keeps the available content width steadier when a page grows long enough to scroll.
The effect is easiest to notice with a centred column. A vertical scrollbar appears, the usable width changes, and the column moves sideways even though its own CSS has stayed the same.
View code
<label><input id="long" type="checkbox"> Make both panels overflow</label>
<div class="panels"><section><h2>auto</h2><div class="panel"><div class="content"><p>A centred column.</p></div></div></section><section><h2>stable</h2><div class="panel stable"><div class="content"><p>A centred column.</p></div></div></section></div>
<p id="widths" role="status"></p><p class="note">Overlay scrollbars may show no reserved gutter. Compare this on a system with classic scrollbars too.</p>button, select { padding: .35rem .6rem; border: 1px solid var(--clg); background: var(--cw); color: var(--cb); }
input[type="range"] { width: min(100%, 18rem); vertical-align: middle; }
h2, h3 { font-size: 1rem; }
.note { color: var(--clg); }
.panels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
.panel { height: 10rem; overflow-y: auto; border: 1px solid var(--clg); }
.stable { scrollbar-gutter: stable; }
.content { padding: .5rem; }
.content p { width: 85%; margin-inline: auto; padding: .5rem; background: var(--cg); }
.long .content { min-height: 24rem; }function measure() {
const panels = [...document.querySelectorAll('.panel')];
document.querySelector('#widths').textContent = `Content widths: auto ${panels[0].clientWidth}px; stable ${panels[1].clientWidth}px.`;
}
document.querySelector('#long').addEventListener('change', event => {
document.querySelector('.panels').classList.toggle('long', event.target.checked);
measure();
});
window.addEventListener('resize', measure);
measure();Reserve scrollbar space with scrollbar-gutter
For the document viewport, put the rule on the root element:
html {
scrollbar-gutter: stable;
}
The scrollbar-gutter reference describes the gutter as space between the inner border edge and the outer padding edge where a scrollbar may sit. With classic scrollbars, stable reserves it for the applicable overflow settings even when the box is not overflowing.
The overflow specification applies the root element’s value to the viewport. A value on body is not propagated there in the same way.
No scrollbar width needs to be guessed. The browser reserves the space appropriate to the scrollbar it uses.
Classic and overlay scrollbars behave differently
The browser and platform determine which scrollbar style is used. That distinction changes what the gutter rule can do:
| Scrollbar type | Effect on layout space | Effect of a stable gutter |
|---|---|---|
| Classic | Occupies space beside the content. | Can reserve that space before overflow occurs. |
| Overlay | Draws over the content. | Does not create a reserved gutter. |
MDN distinguishes these two scrollbar models. A layout can therefore move on one computer and remain steady on another, without a difference in the page’s styles.
Overlay scrollbars also explain why adding the property may produce no visible change during development. Check a configuration with classic scrollbars before judging whether the rule solves the intended shift.
Apply the same choice inside a changing panel
A bounded panel can switch between needing a scrollbar and not needing one:
.panel {
max-height: 30rem;
overflow-y: auto;
scrollbar-gutter: stable;
}
Validation errors or expanded details can push its content past the height limit. Reserving the classic scrollbar’s space avoids a change in the width available inside that panel when the scrollbar appears.
The panel still needs a height constraint and the appropriate overflow behaviour. The gutter property does not make the content scrollable by itself.
For an existing form panel, that local rule is often easier to justify than reserving space across an entire site. It addresses the component whose content changes.
Use both-edges when symmetry matters
A gutter on one side can keep the content width stable without making the two sides visually symmetrical. A centred layout may benefit from a matching gutter opposite the scrollbar:
html {
scrollbar-gutter: stable both-edges;
}
The both-edges definition creates a matching gutter on the opposite inline edge when a gutter exists on one side. It does not add a second scrollbar.
The trade-off is less available width. Use the extra gutter when the balanced inset helps the design, rather than adding it automatically wherever stable is used.
A centred column makes the difference visible
A typical constrained content column can sit inside that viewport:
main {
max-width: 70rem;
margin-inline: auto;
}
The column centres within its available space. Reserving scrollbar space can prevent a sudden width change, while both-edges can balance the inset on either side.
Those are separate decisions. A stable single gutter may be enough for a page whose layout does not need matching empty space on both edges.
A gutter does not require a permanently visible scrollbar
overflow-y: scroll requests a scrollbar even when the content does not overflow. The exact appearance still depends on the browser’s scrollbar model.
With overflow-y: auto, a stable classic gutter can exist without an unnecessary scrollbar being shown. The reserved area is painted as an extension of the padding when there is no scrollbar in it.
That separation is the useful part: keep room for a scrollbar without making its presence permanent. Switching from an older overflow workaround still needs checking against the layout’s existing rules.
Keep scrollbar appearance a separate decision
The scrollbar-width property controls the scrollbar’s width preference. For example, a panel can request a thinner scrollbar:
.panel {
scrollbar-width: thin;
}
That changes a different part of the design from reserving a gutter. It does not replace the scrollbar-gutter rule, and the actual scrollbar dimensions remain browser-controlled.
A narrow scrollbar is not required to solve a layout shift. Keep the appearance choice independent of the decision to reserve space.
Compare short and overflowing versions of the same layout
MDN lists scrollbar-gutter as Baseline 2024. Older browsers may ignore the declaration and retain their ordinary scrollbar behaviour.
Compare the page before and after enough content is added to require scrolling. For a panel, use the actual error messages or expanded content that causes the change.
If a centred column shifts only with classic scrollbars, a stable gutter addresses that specific width change. If the layout already stays steady, the reserved space may add no useful benefit.
For a dialog, check which container actually gains or loses its scrollbar when it opens. A gutter on the dialog’s inner panel cannot reserve space for a disappearing scrollbar on the page behind it.