30% offEnding soon
useLockBodyScrollLoading saved progress…

useLockBodyScroll

A scroll lock freezes the page behind a modal, drawer, or menu so the content underneath cannot move while the overlay is open. useLockBodyScroll is a React hook that does this by setting document.body.style.overflow to hidden while it is active, and restoring the page's original overflow when the component closes or unmounts. The interesting part is not the one-liner — it is what happens when two of them run at once.

Signature

function useLockBodyScroll(locked?: boolean): void;
// locked defaults to true. While it is true, the page cannot scroll.
// The hook returns nothing — it is a side effect tied to the component's life.

Examples

// A modal locks the page while it is open and unlocks when it unmounts.
function Modal({ children }) {
  useLockBodyScroll(); // locks on mount, restores on unmount
  return <div className="backdrop">{children}</div>;
}
// Toggle without unmounting: lock only while `open` is true.
function Drawer({ open }) {
  useLockBodyScroll(open); // true -> page frozen; false -> page scrolls
  return open ? <aside className="drawer">...</aside> : null;
}

Notes

  • Save, then restore. Capture the body's current overflow when you lock and put that exact value back when you unlock. The page may have set overflow for its own reasons, so blindly resetting to '' is a bug.
  • Nested locks must coordinate. Two components can hold a lock at the same time — a dialog that opens a second dialog, or a drawer underneath a modal. The page must stay frozen until the last one releases, not the first. This is the heart of the question.
  • The locked argument is optional. Default it to true. A caller that passes a boolean can turn the lock on and off without mounting or unmounting the component.
  • No timers, no layout math. The hook only reads and writes document.body.style.overflow, which is fully supported in the test environment. You will not need to fake time or mock element geometry.
  • Out of scope here. iOS Safari's touch-scroll quirk and the scrollbar-width layout shift are real; they are covered in the solution's Going further, not required by the tests.