Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import { useState, useLayoutEffect } from 'react';
// Usage
function App(){
// State for our modal
const [modalOpen, setModalOpen] = useState(false);
return (
<div>
<button onClick={() => setModalOpen(true)}>Show Modal</button>
<Content />
{modalOpen && (
<Modal
title="Try scrolling"
content="I bet you you can't! Muahahaha 😈"
onClose={() => setModalOpen(false)}
/>
)}
</div>
);
}
function Modal({ title, content, onClose }){
// Call hook to lock body scroll
useLockBodyScroll();
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal">
<h2>{title}</h2>
<p>{content}</p>
</div>
</div>
);
}
// Hook
function useLockBodyScroll() {
useLayoutEffect(() => {
// Get original value of body overflow
const originalStyle = window.getComputedStyle(document.body).overflow;
// Prevent scrolling on mount
document.body.style.overflow = 'hidden';
// Re-enable scrolling when component unmounts
return () => document.body.style.overflow = originalStyle;
}, []); // Empty array ensures effect is only run on mount and unmount
}
@sstur

This comment has been minimized.

Copy link

commented Jan 18, 2019

It's worth noting that browsers / operating systems that use a non-hiding scrollbar (I think most versions of Windows) will show/hide the scrollbar when you change the body overflow from hidden to visible.

It's a tricky one to work around, but in the past I had some luck with first measuring the width/height of the body and then fixing that size before changing the overflow. That prevents the change of layout when you enable/disable scrolling.

@anyexinglu

This comment has been minimized.

Copy link

commented Jan 19, 2019

It's worth noting that browsers / operating systems that use a non-hiding scrollbar (I think most versions of Windows) will show/hide the scrollbar when you change the body overflow from hidden to visible.

It's a tricky one to work around, but in the past I had some luck with first measuring the width/height of the body and then fixing that size before changing the overflow. That prevents the change of layout when you enable/disable scrolling.

I agree, setting document.body.style.paddingRight may resolve the problem.

@anyexinglu

This comment has been minimized.

Copy link

commented Jan 19, 2019

useLayoutEffect(() => {
    // Prevent scrolling on mount
    document.body.style.overflow = 'hidden';
    // Re-enable scrolling when component unmounts
    return () => (document.body.style.overflow = 'visible');
  }, []); // Empty array ensures effect is only run on mount and unmount

return () => (document.body.style.overflow = 'visible')
It's better to reset document.body.style.overflow into original value, such as auto/ hidden (sometimes isn't visible)

Writing these with hooks is great, which decreases the code lines.

@gragland

This comment has been minimized.

Copy link
Owner Author

commented Jan 19, 2019

@sstur Good point! If the body has a background color then they may still have a white area where the scrollbar was.. but I supposed that is better than actual body content moving.

@gragland

This comment has been minimized.

Copy link
Owner Author

commented Jan 19, 2019

@anyexinglu Happy to accept a pull request (we're now open source at https://github.com/gragland/usehooks) or look at a code example in this gist. I supposed we'd just have to grab the current document.body.style and save to state or ref, and then use that when effect cleans up.

@solutweb

This comment has been minimized.

Copy link

commented Feb 18, 2019

EDIT: First of all, thanks for this. 👍

Saddly doesn't work on mobile 😞.

I tried on my Moto G5s + Chrome and on iPhone 8 Plus + Chrome too 🤔:
uselockbodyscroll

@gragland

This comment has been minimized.

Copy link
Owner Author

commented Feb 19, 2019

@solutweb Ah good point. When I have some time will have to dig into what's going on there. Open to pull requests if you happen to figure it out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.