useScrollLock
Lock scrolling of the element while preserving scrolling within nested scrollable containers.
Usage
Live Editor
function Demo() { const elementRef = useRef<HTMLDivElement>(null); const [locked, setLocked] = useScrollLock(elementRef); const absoluteStyle: CSSProperties = { paddingTop: "0.25rem", paddingBottom: "0.25rem", paddingLeft: "0.5rem", paddingRight: "0.5rem", position: "absolute", }; return ( <div style={{ display: "flex" }}> <div ref={elementRef} style={{ width: 300, height: 300, margin: "auto", borderRadius: "0.25rem", overflow: "scroll", border: "1px solid var(--ifm-color-emphasis-200)", }} > <div style={{ width: 500, height: 400, position: "relative" }}> <div style={{ ...absoluteStyle, top: "0rem", left: "0rem", }} > TopLeft </div> <div style={{ ...absoluteStyle, bottom: "0rem", left: "0rem", }} > BottomLeft </div> <div style={{ ...absoluteStyle, top: "0rem", right: "0rem", }} > TopRight </div> <div style={{ ...absoluteStyle, bottom: "0rem", right: "0rem", }} > BottomRight </div> <div style={{ ...absoluteStyle, top: "33.33333%", left: "33.33333%", }} > Scroll Me </div> </div> </div> <div style={{ width: 280, margin: "auto", paddingLeft: "1rem", display: "flex", flexDirection: "column", gap: 5, }} > <div>Locked: {JSON.stringify(locked)}</div> <button onClick={() => { setLocked(!locked); }} style={{ padding: "8px 16px", border: "1px solid var(--ifm-color-emphasis-300)", backgroundColor: "var(--ifm-background-color)", color: "var(--ifm-color-content)", borderRadius: "4px", cursor: "pointer", }} > {locked ? "Unlock" : "Lock"} </button> </div> </div> ); };
Result
TopLeft
BottomLeft
TopRight
BottomRight
Scroll Me
Locked: false
Dialog Example
Live Editor
function DialogDemo() { const { cloneElement, ReactElement, useRef, useState } = React; const Dialog = ({ trigger }: { trigger: ReactElement }) => { const dialogRef = useRef<HTMLDialogElement>(null); const boxRef = useRef(null); const [isOpen, setIsOpen] = useState(false); const [, setLock] = useScrollLock(document.body); const open = () => { setIsOpen(true); dialogRef.current?.showModal(); setLock(true); }; const close = () => { setIsOpen(false); dialogRef.current?.close(); setLock(false); }; useEventListener("close", () => setIsOpen(false), dialogRef); useClickOutside(boxRef, close); return ( <> {cloneElement(trigger, { onClick: open })} <dialog ref={dialogRef} style={{ padding: 0, border: "none", borderRadius: "8px", boxShadow: "0 10px 25px rgba(0, 0, 0, 0.1)", maxWidth: "500px", width: "90vw", }} > <div ref={boxRef} style={{ padding: "24px", maxHeight: "70vh", overflow: "auto", backgroundColor: "var(--ifm-background-color)", borderRadius: "8px", }} > <h3 style={{ margin: "0 0 16px 0", color: "var(--ifm-color-content)" }}> Dialog with Scrollable Content </h3> <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}> This dialog demonstrates how useScrollLock works. The background scrolling is locked, but you can still scroll within this dialog content. </p> <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}> Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. </p> <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}> Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. </p> <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}> In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. </p> <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}> Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. Sed cursus turpis a purus aliquam condimentum. Nam bibendum cursus dolor. </p> <div style={{ display: "flex", justifyContent: "flex-end", marginTop: "24px", gap: "12px" }}> <button onClick={close} style={{ padding: "8px 16px", border: "1px solid var(--ifm-color-emphasis-300)", backgroundColor: "var(--ifm-background-color)", color: "var(--ifm-color-content)", borderRadius: "4px", cursor: "pointer", }} > Close </button> </div> </div> </dialog> </> ); }; return ( <div style={{ padding: "20px", textAlign: "center" }}> <Dialog trigger={ <button style={{ padding: "12px 24px", border: "1px solid var(--ifm-color-primary)", backgroundColor: "var(--ifm-color-primary)", color: "var(--ifm-color-white)", borderRadius: "4px", cursor: "pointer", fontSize: "16px", }} > Open Dialog </button> } /> <p style={{ marginTop: "16px", color: "var(--ifm-color-content-secondary)", fontSize: "14px", maxWidth: "400px", margin: "16px auto 0" }}> Open the dialog and try scrolling. You'll be able to scroll within the dialog content while background scrolling remains locked. </p> </div> ); }
Result
Open the dialog and try scrolling. You'll be able to scroll within the dialog content while background scrolling remains locked.
API
useScrollLock
Returns
readonly [boolean, (flag: boolean) => void]
: A tuple with the following elements:
- whether scroll is locked.
- A function to update the value of lock state.
Arguments
Argument | Description | Type | DefaultValue |
---|---|---|---|
target | dom element | BasicTarget<HTMLElement> (Required) | - |
initialState | default value | boolean | undefined | false |
BasicTarget
export type BasicTarget<T extends TargetType = Element> = (() => TargetValue<T>) | TargetValue<T> | MutableRefObject<TargetValue<T>>;
TargetValue
type TargetValue<T> = T | undefined | null;
TargetType
type TargetType = HTMLElement | Element | Window | Document | EventTarget;