I have a website with center-aligned DIV. Now, some pages need scrolling, some don't. When I move from one type to another, the appearance of a scrollbar moves the page a few pixels to the side. Is there any way to avoid this without explicitly showing the scrollbars on each page?
26 Answers
overflow-y:scroll is correct, but you should use it with the html tag, not body or else you get a double scrollbar in IE 7
So the correct css would be:
html {
overflow-y: scroll;
}
-
1this can cause problems like double scrollbars when using in combination with fancybox or twitter bootstrap modal – Ruben Apr 01 '14 at 12:41
-
extra info: twitter bootstrap now adds .modal-open to you body when a modal is open – Ruben Sep 02 '14 at 06:58
-
106Worth mentioning that this permanently shows a vertical scrollbar. May not always be acceptable. – rustyx Dec 24 '15 at 10:40
-
2I get a double scrollbar in both Chromium and Firefox. I'm using flex-box; maybe that is causing this... – Garrett Apr 09 '16 at 06:19
-
2@Garrett try the body tag – Ruben Aug 05 '16 at 09:50
-
1Thank you so much!! I'm so glad I read this before trying to work around the issue! – Tyler Brown May 18 '17 at 18:50
-
1@Ruben Using html was causing small shift when a js modal or lighbox is opened. (probably due to scroll bar was getting removed during modal) Adding it to body solved that too. Thank you! – Onur Mar 10 '21 at 12:30
-
HTML tag causes double scrollbars for me, body works. – NickW Nov 17 '21 at 14:19
Wrap the content of your scrollable element into a div and apply padding-left: calc(100vw - 100%);
.
<body>
<div style="padding-left: calc(100vw - 100%);">
Some Content that is higher than the user's screen
</div>
</body>
The trick is that 100vw
represents 100% of the viewport including the scrollbar. If you subtract 100%
, which is the available space without the scrollbar, you end up with the width of the scrollbar or 0
if it is not present. Creating a padding of that width on the left will simulate a second scrollbar, shifting centered content back to the right.
Please note that this will only work if the scrollable element uses the page's entire width, but this should be no problem most of the time because there are only few other cases where you have centered scrollable content.

- 2,520
- 3
- 20
- 23
-
3Would be better applied via a class than an inline style, and will only work in [these browsers](http://caniuse.com/#search=calc), but this is a clever technique - I hadn't realised that `100vw` and `100%` measured different things, so I've learnt something useful from this! – Nick F Jan 25 '16 at 17:48
-
Just as a note. This only works if the scrolling element has the exact same width as the entire window (i.e. the body element). – dallin Dec 03 '16 at 03:09
-
I think this will still cause screen flickr if scrollbar appears after page load, by ajax load or anything. – Zia Ul Rehman Mughal Dec 15 '16 at 06:12
-
Works great and [browser support](http://caniuse.com/#search=calc) looks good now. @ZiaUlRehmanMughal how will it cause screen flicker that wouldn't be caused by adding a scroll bar anyway? – Jason Goemaat Apr 12 '17 at 09:00
-
Well, i was having some issue and i don't remember now :) . If it is working, cheers! – Zia Ul Rehman Mughal Apr 12 '17 at 09:23
-
2You can do the same except instead of adding a margin to the left, you add a negative margin to the right (see https://stackoverflow.com/a/39289453/6015444). – Touniouk Jan 31 '18 at 16:35
-
5This solution will cause similar issues with background color like the answer "width: calc(100vw - 34px);" but on left side. – Maciej Lew Feb 02 '18 at 22:13
-
"most of the time because there are only few other cases where you have centered scrollable content" - this comment is based on absolutely nothing. Lots of scrollable divs don't take up the entire width of the screen, and for them this solution is useless. – Jim Nov 30 '18 at 22:18
-
5I would recommend to add some media queries to this style- `@media screen and (min-width: 800px) {...} `. In cases when content will fit page width. For example on small resolutions there will be unneeded gap, shifting content to the right – MarkosyanArtur Aug 21 '19 at 07:08
-
This is the best answer I've came across for this type of issue, the colouring of my width container was a minor issue but just wrap another div around it and Bob is your mother's brother. – jamheadart Jun 16 '20 at 13:33
-
This new property does the same but without the calculating: .scrollable-element { scrollbar-gutter: stable both-edges; } (https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter) – ironmouse Sep 02 '22 at 14:59
-
The best solution as of now. Someone from the future please notify me when Safari supports `scrollbar-gutter`: https://caniuse.com/mdn-css_properties_scrollbar-gutter – M Imam Pratama Feb 17 '23 at 13:04
-
`scrollbar-gutter` doesn't seem ideal either (even if it's supported). I try to explain why here https://github.com/tailwindlabs/tailwindcss/discussions/11129#discussioncomment-6204566 – Webber Jun 17 '23 at 14:24
-
I used this for some time before I noticed how it *removed the left padding* on some of my other views. The solution was to use `margin-left: calc(100vw - 100%);` instead – Anna Madsen Aug 09 '23 at 08:10
html {
overflow-x: hidden;
margin-right: calc(-1 * (100vw - 100%));
}
Example. Click "change min-height" button.
With calc(100vw - 100%)
we can calculate the width of the scrollbar (and if it is not displayed, it will be 0). Idea: using negative margin-right, we can increase the width of <html>
to this width. You will see a horizontal scroll bar — it should be hidden using overflow-x: hidden
.

- 1,335
- 13
- 11
-
1I had to put the body in a div and apply the margin to the div for it to work (similar to [Rapti's answer](https://stackoverflow.com/a/30293718/6015444).) But this looks so much neater. – Touniouk Jan 31 '18 at 16:33
-
4This works great and doesn't seem to result in unwanted horizontal scrollbars in Chrome or Firefox. Why not just use `calc(100% - 100vw)` instead of multiplying by `-1`? – SystemParadox Jul 10 '19 at 11:43
-
1
I think not. But styling body
with overflow: scroll
should do. You seem to know that, though.

- 138,757
- 24
- 193
- 173
-
4Sounds good... force the page to always show the scrollbar whether it needs it or not, ... then there is no visual change between page types. – eidylon Sep 13 '09 at 16:10
-
1Yes, but IIRC, this shows *both* scrollbars. I don't really need the horizontal one. – Dmitri Nesteruk Sep 14 '09 at 10:47
-
That's true. But there's not much I can do to help you :( Actually, I only consider the first sentence of my reply to be the *real* answer that deals with the question. – Michael Krelin - hacker Sep 14 '09 at 10:59
-
With scroll always being shown, maybe be not good for layout.
Try to limit body width with css3
body {
width: calc(100vw - 34px);
}
vw
is the width of the viewport (see this link for some explanation)
calc
calculate in css3
34px
stands for double scrollbar width (see this for fixed or this to calculate if you don't trust fixed sizes)
-
3This is visually much less intrusive than just forcing scollbars like the accepted answer describes. – Silas Hansen Feb 09 '16 at 09:58
-
4This has worked the best for my needs so far, I also added `padding-left: 34px;` to evenly center my page. – Pat Migliaccio May 10 '16 at 19:07
-
1Why are we using double scrollbar width here? why not only 17px or 20px as suggested by the link provided in answer? – Zia Ul Rehman Mughal Dec 15 '16 at 06:18
-
3This will cause issues if you have navbar/header with different color than body background or bottom border etc. – Zia Ul Rehman Mughal Dec 15 '16 at 06:44
-
1bad solution because of touch screens without scrollbars. On small screens there will be weird empty space – MarkosyanArtur Aug 21 '19 at 07:11
-
Dear @MarkosyanArtur maybe this solution is not what you're searching. But most people think it's good. For small screens calculate the width of scrollbars in link posted above in answer. – Moesio Aug 21 '19 at 17:09
If changing size or after loading some data it is adding the scroll bar then you can try following, create class and apply this class.
.auto-scroll {
overflow-y: overlay;
overflow-x: overlay;
}

- 294
- 2
- 3
-
1`overflow: overlay` isn't supported in firefox according to [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow). "Only supported in WebKit-based (e.g., Safari) and Blink-based (e.g., Chrome or Opera) browsers." – Erik Joling Sep 07 '20 at 08:20
body {
scrollbar-gutter: stable both-edges;
}
New css spec that will help with scrollbar repositioning is on its way: https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter

- 760
- 9
- 11
I don't know if this is an old post, but i had the same problem and if you want to scroll vertically only you should try overflow-y:scroll
Summary
I see three ways - each with their own quirks:
- scrollbar-gutter as mentioned by Markus T.
- overflow: overlay as mentioned by kunalkamble
- Add spacing with
calc(100vw - 100%)
as mentioned Rapti
Here is a StackBlitz demo
Press the "Toggle height" to see the content shift.
scrollbar-gutter
This has limited support but with a @support
media query we can use a combination of this and overflow-y: scroll
:
html {
overflow-y: scroll;
}
@supports (scrollbar-gutter: stable) {
html {
overflow-y: auto;
scrollbar-gutter: stable;
}
}
In this way content will never shift.
The "problem" with this solution is that there is always a fixed space for the scrollbar.
overflow: overlay Limited support and it obviously hides anything it overlays. Special care is needed to make sure nothing vital is hidden (also on zoom and text size changes).
Can be combined with scrollbar-gutter:
html {
overflow-y: scroll;
}
@supports (scrollbar-gutter: stable) {
html {
overflow-y: auto;
scrollbar-gutter: stable;
}
}
@supports (overflow-y: overlay) {
html {
overflow-y: overlay;
scrollbar-gutter: auto;
}
}
It is possible to do some negative margin and overflow-x: hidden
but this has a risk of hiding vital content under certain situations. Small screen, custom font/zoom size, browser extensions, etc.
calc(100vw - 100%)
This can be done with RTL support like this:
html[dir='ltr'] main {
padding-left: calc(100vw - 100%);
}
html[dir='rtl'] main {
padding-right: calc(100vw - 100%);
}
Where <main>
in this case would be the container for the centered content.
Content here will not shift as long as the centered container is smaller than <main>
. But as soon as it is 100% of the container a padding will be introduced. See the StackBlitz demo and click "Toggle width".
The "problem" with this solution is that you need media queries to prevent padding on "small screens" and that even on small screens - when the scrollbar should be visible - some shifting will occur because there is no room for 100% content and a scrollbar.
Conclusion
Use scrollbar-gutter perhaps combined with overlay. If you absolutely don't want empty spacing, try the calc
solution with media queries.

- 542
- 7
- 10
Simply setting the width of your container element like this will do the trick
width: 100vw;
This will make that element ignore the scrollbar and it works with background color or images.

- 197
- 2
- 12
-
1Setting this on the "body" element works, but adds a horizontal scroll bar (using Firefox). Any other reasons why this solution is not a good idea? – TomDogg Jul 22 '20 at 14:21
-
-
1I had to use: `box-sizing: border-box; // so width will be calculated with padding` , `padding-right: 1.2rem; // escape scroll-bar width` and `overflow-x: hidden; // hide horizontal scrollbar` on html with `width: 100vw`. – Ketan Chaudhari Oct 24 '21 at 07:13
@kashesandr's solution worked for me but to hide horizontal scrollbar I added one more style for body. here is complete solution:
CSS
<style>
/* prevent layout shifting and hide horizontal scroll */
html {
width: 100vw;
}
body {
overflow-x: hidden;
}
</style>
JS
$(function(){
/**
* For multiple modals.
* Enables scrolling of 1st modal when 2nd modal is closed.
*/
$('.modal').on('hidden.bs.modal', function (event) {
if ($('.modal:visible').length) {
$('body').addClass('modal-open');
}
});
});
JS Only Solution (when 2nd modal opened from 1st modal):
/**
* For multiple modals.
* Enables scrolling of 1st modal when 2nd modal is closed.
*/
$('.modal').on('hidden.bs.modal', function (event) {
if ($('.modal:visible').length) {
$('body').addClass('modal-open');
$('body').css('padding-right', 17);
}
});

- 1,188
- 20
- 46
Extending off of Rapti's answer, this should work just as well, but it adds more margin to the right side of the body
and hides it with negative html
margin, instead of adding extra padding that could potentially affect the page's layout. This way, nothing is changed on the actual page (in most cases), and the code is still functional.
html {
margin-right: calc(100% - 100vw);
}
body {
margin-right: calc(100vw - 100%);
}

- 1,314
- 16
- 24
-
1I found that on chrome this adds a horizontal scroll on pages that have a vertical scrollbar on my bootstrap site. – Ginger Squirrel May 30 '18 at 08:42
-
Have you tried looking into why? Check the element inspector and see if Bootstrap messes with margins, scrollbars, or anything like that. – Grant Gryczan May 31 '18 at 22:16
-
1It probably was something to do with the 15px margins that bootstrap puts on everything, when inspecting the page the container margin falls under the scrollbar. I applied `overflow-x: hidden` and that fixed it. – Ginger Squirrel Jun 01 '18 at 10:44
I've solved the issue on one of my websites by explicitly setting the width of the body in javascript by the viewport size minus the width of the scrollbar. I use a jQuery based function documented here to determine the width of the scrollbar.
<body id="bodyid>
var bodyid = document.getElementById('bodyid');
bodyid.style.width = window.innerWidth - scrollbarWidth() + "px";

- 73
- 1
-
10
-
21
-
1
-
-
It's a very unreliable solution. First noticeable errors: 1. You could (and should) get the body element by using `document.body` instead of `document.getElementById` 2. Setting styles directly is considered a bad practice. Check the [setProperty](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/setProperty) method. 3. This should be inside of some listener and being called during the first render: ```lang-js function handle() { document.body.style.setProperty('width', '100%'); } document.body.addEventListener('resize', handle); handle() ``` – luuchorocha Jan 30 '22 at 07:02
Expanding on the answer using this:
body {
width: calc(100vw - 17px);
}
One commentor suggested adding left-padding as well to maintain the centering:
body {
padding-left: 17px;
width: calc(100vw - 17px);
}
But then things don't look correct if your content is wider than the viewport. To fix that, you can use media queries, like this:
@media screen and (min-width: 1058px) {
body {
padding-left: 17px;
width: calc(100vw - 17px);
}
}
Where the 1058px = content width + 17 * 2
This lets a horizontal scrollbar handle the x overflow and keeps the centered content centered when the viewport is wide enough to contain your fixed-width content

- 31
- 1
If the width of the table won't change, you can set the width of the element (such as tbody) that contains the scrollbar > 100% (allowing extra space for the scrollbar) and set overflow-y to "overlay" (so that the scrollbar stays fixed, and won't shift the table left when it appears). Also set a fixed height for the element with the scrollbar, so the scrollbar will appear once the height is exceeded. Like so:
tbody {
height: 100px;
overflow-y: overlay;
width: 105%
}
Note: you will have to manually adjust the width % as the % of space the scrollbar takes up will be relative to your table width (ie: smaller width of table, more % required to fit the scrollbar, as it's size in pixels is constant)
A dynamic table example:
function addRow(tableID)
{
var table = document.getElementById(tableID);
var rowCount = table.rows.length;
var row = table.insertRow(rowCount);
var colCount = table.rows[0].cells.length;
for(var i=0; i<colCount; i++)
{
var newRow = row.insertCell(i);
newRow.innerHTML = table.rows[0].cells[i].innerHTML;
newRow.childNodes[0].value = "";
}
}
function deleteRow(row)
{
var table = document.getElementById("data");
var rowCount = table.rows.length;
var rowIndex = row.parentNode.parentNode.rowIndex;
document.getElementById("data").deleteRow(rowIndex);
}
.scroll-table {
border-collapse: collapse;
}
.scroll-table tbody {
display:block;
overflow-y:overlay;
height:60px;
width: 105%
}
.scroll-table tbody td {
color: #333;
padding: 10px;
text-shadow: 1px 1px 1px #fff;
}
.scroll-table thead tr {
display:block;
}
.scroll-table td {
border-top: thin solid;
border-bottom: thin solid;
}
.scroll-table td:first-child {
border-left: thin solid;
}
.scroll-table td:last-child {
border-right: thin solid;
}
.scroll-table tr:first-child {
display: none;
}
.delete_button {
background-color: red;
color: white;
}
.container {
display: inline-block;
}
body {
text-align: center;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="test_table.css">
</head>
<body>
<h1>Dynamic Table</h1>
<div class="container">
<table id="data" class="scroll-table">
<tbody>
<tr>
<td><input type="text" /></td>
<td><input type="text" /></td>
<td><input type="button" class="delete_button" value="X" onclick="deleteRow(this)"></td>
</tr>
</tbody>
</table>
<input type="button" value="Add" onclick="addRow('data')" />
</div>
<script src="test_table.js"></script>
</body>
</html>

- 193
- 10
I tried to fix likely the same issue which caused by twitter bootstrap .modal-open
class applied to body
. The solution html {overflow-y: scroll}
doesn't help. One possible solution I found is to add {width: 100%; width: 100vw}
to the html
element.

- 1,521
- 28
- 35
-
1
-
1
-
You should explain why are you setting width twice? `html { width: 100vw; }` worked for me – MD TAREQ HASSAN Oct 29 '19 at 01:20
-
I use to have that problem, but the simples way to fix it is this (this works for me):
on the CSS file type:
body{overflow-y:scroll;}
as that simple! :)

- 342
- 3
- 10
The solutions posted using calc(100vw - 100%) are on the right track, but there is a problem with this: You'll forever have a margin to the left the size of the scrollbar, even if you resize the window so that the content fills up the entire viewport.
If you try to get around this with a media query you'll have an awkward snapping moment because the margin won't progressively get smaller as you resize the window.
Here's a solution that gets around that and AFAIK has no drawbacks:
Instead of using margin: auto to center your content, use this:
body {
margin-left: calc(50vw - 500px);
}
Replace 500px with half the max-width of your content (so in this example the content max-width is 1000px). The content will now stay centered and the margin will progressively decrease all the way until the content fills the viewport.
In order to stop the margin from going negative when the viewport is smaller than the max-width just add a media query like so:
@media screen and (max-width:1000px) {
body {
margin-left: 0;
}
}
Et voilà!

- 33
- 5
After trying most of the above CSS or JS-based solutions that haven't worked in my case, just wanted to add up to it. My solution worked for the case where the scrollbar had to disappear on an event (e.g. a button click, cause you've just opened a full-screen menu that should block the page from being scrollable).
This should work when the below styles are applied to the element that turns overflow-y
to hidden
(in my case it's the body
tag):
body {
overflow-x: hidden;
width: 100vw;
margin-right: calc(100vw - 100%);
}
Explanation: The width
of your body
tag is 100vw
(so it includes the scrollbar's width).
By setting the margin-right
, the margin only gets applied if your vertical scrollbar is visible (so your page content isn't actually under the scrollbar), meaning the page content will not reposition once overflow-y
has changed.
Note: this solution only works for the pages that are not horizontally-scrollable.
Tested on Chrome 89.0, Firefox 87.0, Safari 14.0.3
Update: unfortunately it only works with centered container that doesn't take 100% width - otherwise the scrollbar overlays the piece of content on the right.

- 158
- 4
I know the question is very old, but there is a new better method.
scrollbar-gutter: stable;

- 182
- 7
My approach is to make the track transparent. The scroll bar thumb color is #C1C1C1 to match the default scrollbar thumb color. You can make it anything you prefer :)
Try this:
html {
overflow-y: scroll;
}
body::-webkit-scrollbar {
width: 0.7em;
background-color: transparent;
}
body::-webkit-scrollbar-thumb {
background: #C1C1C1;
height:30px;
}
body::-webkit-scrollbar-track-piece
{
display:none;
}

- 2,565
- 4
- 26
- 35
Since I haven't found my solution here I would like to add it:
I did not want a permanent scrollbar (accepted solution) and I also decided to not use negative margins. They didn't (instantly) work for me in chrome and I also did not want to have content possibly disappearing below the scrollbar.
So this is a padding solution.
My web page consists of three parts:
- Header (content is left aligned)
- MainContent (content is centered)
- Footer (content is left and right aligned)
Since the header would look bad with a left padding and since the logo should stay in the corner of the page, I kept it unchanged since the appearing of a scrollbar does not affect it in most cases (except when window width is very small).
Since an even padding is acceptable for both the MainContent and the footer I used only for those both containers the following css:
.main-content, .footer {
/*
* Set the padding to the maximum scrollbar width minus the actual scrollbar width.
* Maximum scrollbar width is 17px according to: https://codepen.io/sambible/post/browser-scrollbar-widths
*/
padding-right: calc(17px - (100vw - 100%));
padding-left: 17px;
}
This will keep the MainContent in the exact center and also work for all scrollbar width up to 17px. One could add a media query removing these paddings for mobile devices that have an overlay scrollbar. This solution is similar to only adding the left padding and setting the width to "width: calc(100vw - 17px);". I cannot say if it would behave equally in all cases though.

- 51
- 5
I tried overflow scroll but it didn't work for my case. the scroll bar still adds some kind of (white) padding. what works is changing the width from 100vw to 100%, but for the height it is ok to use 100vh. so this:
const Wrapper = styled.div`
min-height: 100vh
`
const Parent = styled.div`
width: 100%
`
const Children = styled.div`
width: 100%
`
Edit I've set the width twice because the parent component held a sidebar, and the children. Depending on your use case, you can set it once.

- 167
- 1
- 5
I used some jquery to solve this
$('html').css({
'overflow-y': 'hidden'
});
$(document).ready(function(){
$(window).load(function() {
$('html').css({
'overflow-y': ''
});
});
});
-
3You're likely getting poor reception on your answer because this solution requires an entire JavaScript framework. "jQuery all the things!" – Paul Lammertsma May 30 '16 at 16:36
-
3yeah, jQuery is definitely the way to go - http://www.doxdesk.com/img/updates/20091116-so-large.gif – Annonymous Jun 01 '16 at 15:59
-
Welcome to Stack Overflow! While this code snippet may solve the question, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, this reduces the readability of both the code and the explanations! – Andrew Myers May 31 '16 at 20:06
Contrary to the accepted answer which suggests a permanent scroll bar on the browser window even if the content doesn't overflow the screen, I would prefer using:
html{
height:101%;
}
This is because the appearance of scroll bar makes more sense if the content actually overflows.

- 2,139
- 2
- 23
- 40
-
2At least last sentence is wrong in your answer. It makes sense similar with excess empty page printed by printer. – vp_arth Dec 21 '16 at 18:39