useLockedBody()
This React hook is used to block the scrolling of the page.
A good example of a use case is when you need to open a modal.
For flexibility, this hook offers 2 APIs:
- Use it as we would use a useState (example 1)
- Use it with our own logic, coming from a props or redux for example (example 2)
Finally, you can optionally change the reflow padding if you have another sidebar size than the default (15px)
The Hook
1import { useEffect, useState } from 'react'23import { useIsomorphicLayoutEffect } from 'usehooks-ts'45type UseLockedBodyOutput = [boolean, (locked: boolean) => void]67function useLockedBody(8 initialLocked = false,9 rootId = '___gatsby', // Default to `___gatsby` to not introduce breaking change10): UseLockedBodyOutput {11 const [locked, setLocked] = useState(initialLocked)1213 // Do the side effect before render14 useIsomorphicLayoutEffect(() => {15 if (!locked) {16 return17 }1819 // Save initial body style20 const originalOverflow = document.body.style.overflow21 const originalPaddingRight = document.body.style.paddingRight2223 // Lock body scroll24 document.body.style.overflow = 'hidden'2526 // Get the scrollBar width27 const root = document.getElementById(rootId) // or root28 const scrollBarWidth = root ? root.offsetWidth - root.scrollWidth : 02930 // Avoid width reflow31 if (scrollBarWidth) {32 document.body.style.paddingRight = `${scrollBarWidth}px`33 }3435 return () => {36 document.body.style.overflow = originalOverflow3738 if (scrollBarWidth) {39 document.body.style.paddingRight = originalPaddingRight40 }41 }42 }, [locked])4344 // Update state if initialValue changes45 useEffect(() => {46 if (locked !== initialLocked) {47 setLocked(initialLocked)48 }49 // eslint-disable-next-line react-hooks/exhaustive-deps50 }, [initialLocked])5152 return [locked, setLocked]53}5455export default useLockedBody
Usage
1import { CSSProperties, useState } from 'react'23import { useLockedBody } from 'usehooks-ts'45const fixedCenterStyle: CSSProperties = {6 position: 'fixed',7 top: '50%',8 left: '50%',9 transform: 'translate(-50%, -50%)',10}1112const fakeScrollableStyle: CSSProperties = {13 minHeight: '150vh',14 background: 'linear-gradient(palegreen, palegoldenrod, palevioletred)',15}1617// Example 1: useLockedBody as useState()18export default function App() {19 const [locked, setLocked] = useLockedBody(false, 'root')2021 const toggleLocked = () => {22 setLocked(!locked)23 }2425 return (26 <div style={fakeScrollableStyle}>27 <button style={fixedCenterStyle} onClick={toggleLocked}>28 {locked ? 'unlock scroll' : 'lock scroll'}29 </button>30 </div>31 )32}3334// Example 2: useLockedBody with our custom state35export function App2() {36 const [locked, setLocked] = useState(false)3738 const toggleLocked = () => {39 setLocked(!locked)40 }4142 useLockedBody(locked, 'root')4344 return (45 <div style={fakeScrollableStyle}>46 <button style={fixedCenterStyle} onClick={toggleLocked}>47 {locked ? 'unlock scroll' : 'lock scroll'}48 </button>49 </div>50 )51}
See a way to make this page better?
Edit there »