All hooks / Browser APIs
useScrollLock
Stops the page from scrolling while locked is true, e.g. behind a modal.
Sets overflow: hidden on <body> and pads it by the scrollbar width so the layout does not shift. Locks are counted, so nested modals work: the page unlocks when the last one releases. Restores the original styles. Applied in a layout effect, so there is no frame where the page can scroll.
Import
import { useScrollLock } from '@vincent.ri/react-tils'
// or only this hook
import { useScrollLock } from '@vincent.ri/react-tils/useScrollLock'Usage
function Modal({ open, children }: { open: boolean; children: React.ReactNode }) {
useScrollLock(open)
return open ? <div role="dialog" aria-modal="true">{children}</div> : null
}Live demo
Opens a modal; the page can't scroll behind it.
When to use it
A modal, drawer, or full-screen menu is open.
When not to
You use the native <dialog> with showModal(), where CSS body:has(dialog[open]) { overflow: hidden } is enough. To lock scrolling inside an element, use CSS overscroll-behavior: contain.
API
function useScrollLock(locked?: boolean): void| Parameter | Description |
|---|---|
locked | Lock while true. Default true. |
Browser support
Uses only long-established browser features, so it works wherever React does.
First version with support, from MDN's compatibility data (web-features 3.42.0, @mdn/browser-compat-data 8.1.5).
Coming from another library?
Other libraries call this useLockBodyScroll.