// https://usehooks-ts.com/react-hook/use-locked-body import { useEffect, useState } from 'react' import { useIsomorphicLayoutEffect } from 'usehooks-ts' type UseLockedBodyOutput = [boolean, (locked: boolean) => void] function useLockedBody( initialLocked = false, rootId = '___gatsby' // Default to `___gatsby` to not introduce breaking change ): UseLockedBodyOutput { const [locked, setLocked] = useState(initialLocked) // Do the side effect before render useIsomorphicLayoutEffect(() => { if (!locked) { return } // Save initial body style const originalOverflow = document.body.style.overflow const originalPaddingRight = document.body.style.paddingRight // Lock body scroll document.body.style.overflow = 'hidden' // Get the scrollBar width const root = document.getElementById(rootId) // or root const scrollBarWidth = root ? root.offsetWidth - root.scrollWidth : 0 // Avoid width reflow if (scrollBarWidth) { document.body.style.paddingRight = `${scrollBarWidth}px` } return () => { document.body.style.overflow = originalOverflow if (scrollBarWidth) { document.body.style.paddingRight = originalPaddingRight } } }, [locked]) // Update state if initialValue changes useEffect(() => { if (locked !== initialLocked) { setLocked(initialLocked) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialLocked]) return [locked, setLocked] } export default useLockedBody