Loading...
Please waitLoading...
Please waitpnpm dlx uselab@latest add use-scroll-blocker
import * as React from "react"
import { useScrollBlocker } from "@/hooks/use-scroll-blocker"
function Modal() {
const [isOpen, setIsOpen] = React.useState(false)
const { block, unblock } = useScrollBlocker()
const handleOpen = () => {
setIsOpen(true)
block()
}
const handleClose = () => {
setIsOpen(false)
unblock()
}
return (
<>
<button onClick={handleOpen}>Open Modal</button>
{isOpen && (
<div className="modal">
<button onClick={handleClose}>Close</button>
</div>
)}
</>
)
}The hook provides manual control over scroll blocking, allowing you to block and unblock scrolling at the right moments in your component lifecycle.
useScrollBlocker()Returns methods plus the active state for this hook instance:
| Name | Type | Description |
|---|---|---|
isBlocked | boolean | Whether this instance currently holds an active scroll block |
block | () => void | Block background scrolling and preserve position |
unblock | () => void | Unblock background scrolling and restore position |
toggle | () => void | Calls unblock if blocked, otherwise block for this instance |
import { useScrollBlocker } from "@/hooks/use-scroll-blocker"
function MyModal() {
const [open, setOpen] = React.useState(false)
const { block, unblock } = useScrollBlocker()
React.useEffect(() => {
if (open) {
block()
} else {
unblock()
}
}, [open, block, unblock])
return (
<>
<button onClick={() => setOpen(true)}>Open</button>
{open && (
<div className="modal">
<button onClick={() => setOpen(false)}>Close</button>
</div>
)}
</>
)
}import { useScrollBlocker } from "@/hooks/use-scroll-blocker"
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"
function MyDialog() {
const [open, setOpen] = React.useState(false)
const { block, unblock } = useScrollBlocker()
React.useEffect(() => {
if (open) {
block()
} else {
unblock()
}
}, [open, block, unblock])
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger>Open Dialog</DialogTrigger>
<DialogContent>
<p>Dialog content</p>
</DialogContent>
</Dialog>
)
}function App() {
const [modal1Open, setModal1Open] = React.useState(false)
const [modal2Open, setModal2Open] = React.useState(false)
const { block, unblock } = useScrollBlocker()
React.useEffect(() => {
if (modal1Open || modal2Open) {
block()
} else {
unblock()
}
}, [modal1Open, modal2Open, block, unblock])
return (
<>
<button onClick={() => setModal1Open(true)}>Open Modal 1</button>
<button onClick={() => setModal2Open(true)}>Open Modal 2</button>
{/* modals */}
</>
)
}block() are handled correctly — scroll only unblocks when all instances call unblock()Click the button above to open a modal. Notice how the background page cannot be scrolled while scroll blocking is active.
block() when opening a modal or popupunblock() when closing itScroll this page up and down, then open the modal. Notice how the background stays fixed while the modal is open. This prevents users from accidentally scrolling the content behind modals.