Skip to content
Sensors

useScrollLock

Lock and unlock scrolling on a target element or document.body. Useful for modals, drawers, and overlays that need to prevent background scrolling.

useScrollLock

Lock and unlock page scrolling. Try scrolling the page after locking.

Unlocked
import {
const useScrollLock: (element?: MaybeEventTarget<HTMLElement>, initialState?: MaybeObservable<boolean>, options?: DeepMaybeObservable<ConfigurableWindow>) => UseScrollLockReturn
useScrollLock
} from "@usels/web";
function
function Component(): JSX.Element
Component
() {
const {
const isLocked$: any

Current scroll lock state

isLocked$
,
const lock: () => void

Lock scrolling

lock
,
const unlock: () => void

Unlock scrolling

unlock
,
const toggle: () => void

Toggle lock state

toggle
} =
function useScrollLock(element?: MaybeEventTarget<HTMLElement>, initialState?: MaybeObservable<boolean>, options?: DeepMaybeObservable<ConfigurableWindow>): UseScrollLockReturn

Framework-agnostic reactive scroll-lock controller. Applies overflow: hidden and scrollbar-width padding to the target element (or document.body when no target is provided), and toggles the touchmove preventDefault listener for iOS Safari support.

useScrollLock
();
return (
<
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
<
JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
>Scroll is {
const isLocked$: any

Current scroll lock state

isLocked$
.
any
get
() ? "locked" : "unlocked"}</
JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
>
<
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const toggle: () => void

Toggle lock state

toggle
}>Toggle scroll lock</
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
</
JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
);
}

By default, scroll lock is applied to document.body. Pass any element via the first argument to scope locking to that element.

// @noErrors
import { useRef$ } from "@usels/core";
import { useScrollLock } from "@usels/web";
function Component() {
const el$ = useRef$<HTMLDivElement>();
const { isLocked$, toggle } = useScrollLock(el$);
return (
<div ref={el$} style={{ overflow: "auto", height: 300 }}>
<button onClick={toggle}>{isLocked$.get() ? "Unlock" : "Lock"} scroll</button>
{/* long content */}
</div>
);
}

Pass true as the second argument to start with scrolling locked on mount.

// @noErrors
import { useScrollLock } from "@usels/web";
// ---cut---
const { isLocked$, unlock } = useScrollLock(undefined, true);
// scrolling is locked immediately on mount

Pass an Observable as initialState to seed the lock state reactively.

// @noErrors
import { observable } from "@usels/core";
import { useScrollLock } from "@usels/web";
// ---cut---
const locked$ = observable(false);
const { isLocked$ } = useScrollLock(undefined, locked$);
ParameterTypeDescription
elementMaybeEventTarget<HTMLElement> (optional)-
initialStateMaybeObservable<boolean> | undefined (optional)-
optionsConfigurableWindow (optional)-

UseScrollLockReturn

NameTypeDescription
isLocked$ObservableBoolean<boolean>Current scroll lock state
lock() => voidLock scrolling
unlock() => voidUnlock scrolling
toggle() => voidToggle lock state

View on GitHub