useScrollLock
A hook to lock the scroll of the body
Loading...
Installation
npx shadcn@latest add @hooks/use-scroll-lockpnpm dlx shadcn@latest add @hooks/use-scroll-lockyarn dlx shadcn@latest add @hooks/use-scroll-lockbun x shadcn@latest add @hooks/use-scroll-lockCopy and paste the following code into your project.
import { useEffect, useLayoutEffect } from 'react'
import { isBrowser } from '@/registry/lib/is-browser'
/**
* Custom hook that uses either `useLayoutEffect` or `useEffect` based on the environment (client-side or server-side).
* @param {Function} effect - The effect function to be executed.
* @param {Array<any>} [dependencies] - An array of dependencies for the effect (optional).
* @public
* @see [Documentation](https://usehooks-ts.com/react-hook/use-isomorphic-layout-effect)
* @example
* ```tsx
* useIsomorphicLayoutEffect(() => {
* // Code to be executed during the layout phase on the client side
* }, [dependency1, dependency2]);
* ```
*/
export const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect/**
* A library that checks if the code is running in a browser.
*/
export const isBrowser = typeof window !== 'undefined'import { useCallback, useRef, useState } from 'react'
import { useIsomorphicLayoutEffect } from '@/registry/hooks/use-isomorphic-layout-effect'
import { isBrowser } from '@/registry/lib/is-browser'
interface UseScrollLockOptions {
autoLock?: boolean
lockTarget?: HTMLElement | string
widthReflow?: boolean
}
interface UseScrollLockReturn {
isLocked: boolean
lock: () => void
unlock: () => void
}
interface OriginalStyle {
overflow: CSSStyleDeclaration['overflow']
paddingRight: CSSStyleDeclaration['paddingRight']
}
export function useScrollLock(
options: UseScrollLockOptions = {},
): UseScrollLockReturn {
const { autoLock = true, lockTarget, widthReflow = true } = options
const [isLocked, setIsLocked] = useState(false)
const target = useRef<HTMLElement | null>(null)
const originalStyle = useRef<OriginalStyle | null>(null)
const lock = useCallback(() => {
if (target.current) {
const { overflow, paddingRight } = target.current.style
// Save the original styles
originalStyle.current = { overflow, paddingRight }
// Prevent width reflow
if (widthReflow) {
// Use window inner width if body is the target as global scrollbar isn't part of the document
const offsetWidth =
target.current === document.body
? window.innerWidth
: target.current.offsetWidth
// Get current computed padding right in pixels
const currentPaddingRight =
Number.parseInt(
window.getComputedStyle(target.current).paddingRight,
10,
) || 0
const scrollbarWidth = offsetWidth - target.current.scrollWidth
target.current.style.paddingRight = `${scrollbarWidth + currentPaddingRight}px`
}
// Lock the scroll
target.current.style.overflow = 'hidden'
setIsLocked(true)
}
}, [widthReflow])
const unlock = useCallback(() => {
if (target.current && originalStyle.current) {
target.current.style.overflow = originalStyle.current.overflow
// Only reset padding right if we changed it
if (widthReflow) {
target.current.style.paddingRight = originalStyle.current.paddingRight
}
}
setIsLocked(false)
}, [widthReflow])
useIsomorphicLayoutEffect(() => {
if (!isBrowser) return
if (lockTarget) {
target.current =
typeof lockTarget === 'string'
? document.querySelector(lockTarget)
: lockTarget
}
if (!target.current) {
target.current = document.body
}
if (autoLock) {
lock()
}
return () => {
unlock()
}
}, [autoLock, lockTarget, widthReflow])
return { isLocked, lock, unlock }
}API
interface UseScrollLockOptions {
autoLock?: boolean
lockTarget?: HTMLElement | string
widthReflow?: boolean
}
interface UseScrollLockReturn {
isLocked: boolean
lock: () => void
unlock: () => void
}
/**
* A hook to lock the scroll of the body
* @param options - The options for the hook
* @param options.autoLock - Whether to automatically lock the scroll when the component is mounted
* @param options.lockTarget - The target element to lock the scroll on
* @param options.widthReflow - Whether to reflow the width of the body when the scroll is locked
* @returns The scroll lock state and the functions to lock and unlock the scroll
*/
function useScrollLock(options?: UseScrollLockOptions): UseScrollLockReturnCredits
Last updated on