useScrollLock
Layout & observersLock the page scroll for modals, drawers, and menus — iOS-aware, nested-lock counted, with scroll-position restore
Install
$ npm install @usefy/use-scroll-lockQuick start
useScrollLock.tsx
import { useEffect, useState } from "react";
import { useScrollLock } from "@usefy/use-scroll-lock";
function Modal() {
const [open, setOpen] = useState(false);
const { lock, unlock } = useScrollLock();
useEffect(() => {
if (open) lock();
else unlock();
}, [open, lock, unlock]);
return (
<>
<button onClick={() => setOpen(true)}>Open modal</button>
{open && (
<div role="dialog" aria-modal="true">
<button onClick={() => setOpen(false)}>Close</button>
</div>
)}
</>
);
}API reference
useScrollLock(options?)
Returns { lock, unlock, isLocked } for locking the page scroll.
Options — UseScrollLockOptions
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | When true, hold a lock automatically for as long as it stays true (locks on mount, releases on unmount / when it flips false). Let it own the lock — don't also call lock/unlock on the same instance |
Returns — UseScrollLockReturn
| Property | Type | Description |
|---|---|---|
lock | () => void | Acquire a lock for this instance. Stable (useCallback) and idempotent — never double-counts against the shared counter |
unlock | () => void | Release this instance's lock. Stable and idempotent. The body is only restored once the last lock across all instances is gone |
isLocked | boolean | Whether this instance currently holds a lock (the body may still be locked by another instance) |
Also exported: the isIOS and getScrollbarWidth helpers, and the UseScrollLockOptions / UseScrollLockReturn types.
Go deeper
This page is the quick reference. For every example, prop, and edge case, read the full README — or open Storybook to change props live.