custom hooks

useLockBodyScroll( )

The code provided in this article is simplified as much as possible to avoid overwhelming the reader with code and to convey the essence of the problem and its solution. In interactive examples, heading tags (`h1...h6`) are replaced with span tags to prevent potential conflicts with the main page structure.

Explanation

The custom hook useLockBodyScroll provides a convenient solution for managing scroll behavior within React components, ensuring a smoother user experience. By preventing page scrolling when a specific component is mounted, this hook helps maintain user focus and context.

Use cases for this hook include scenarios where developers need to display modal dialogs, pop-up notifications, or other overlays that require immediate user attention. By temporarily locking the page scroll, developers ensure that users remain engaged with the displayed content, minimizing distractions and potential confusion.

Moreover, the use of useLockBodyScroll contributes to improved accessibility, particularly for users with disabilities who rely on keyboard navigation or screen readers. By preventing accidental navigation away from important UI elements, developers can create a more inclusive and user-friendly experience for all users.

In summary, useLockBodyScroll simplifies scroll control within React components, reducing redundant code and errors while enhancing the end-user experience. Its seamless integration enriches development workflows and contributes to the creation of more accessible and intuitive applications.

Usage Cases

  • Modal Components: Prevent page scrolling when displaying modal dialogs or pop-up overlays to maintain user focus and prevent distractions.
  • Dropdown Menus: Ensure a seamless user experience by locking page scrolling when dropdown menus are open, allowing users to interact with menu options without interference.
  • Mobile Navigation: Enhance mobile navigation menus by preventing background scrolling when the menu is open, providing smoother navigation and improving usability.
  • Fullscreen Views: Implement fullscreen views or slideshows with locked scrolling to create immersive experiences without distractions from page scrolling.
  • Interactive Widgets: Improve the usability of interactive widgets, such as calendars or sliders, by preventing unintended scrolling while users interact with the widget's controls.
  • Scrollable Content: Temporarily disable page scrolling when displaying content that requires user attention, such as tutorials or walkthroughs, ensuring users can focus on the content without interruptions.

Creation

Code implementation
1const useLockBodyScroll = (): void => {
2 // If it's crucial to lock the scroll as early as possible,
3 // you can consider using `useLayoutEffect` instead of `useEffect`.
4 // `useLayoutEffect` fires synchronously after all DOM mutations
5 // but before the browser updates the screen.
6 // It ensures that any layout effects are applied
7 // before the user sees the changes on the screen
8 useEffect(() => {
9 // Get the current window overflow value
10 const originalStyle: string = window.getComputedStyle(
11 document.body
12 ).overflow;
13
14 // Disable scrolling on the document body on mount
15 document.body.style.overflow = 'hidden';
16
17 // Return a cleanup function to enable scrolling when the component unmounts
18 return () => {
19 document.body.style.overflow = originalStyle || '';
20 };
21 }, []); // The dependency array is empty, so this `useEffect` runs only once on component mount
22};
23
24export default useLockBodyScroll;
25

Reference

Code implementation
1useLockBodyScroll();
2

To utilize the useLockBodyScroll hook in a component, simply call it within the component's function body during rendering.

Parameters

This hook doesn't accept any parameters.

Return Values

This hook doesn't return any value.

Example Usages

Scrollable Menu

This example demonstrates the use of the useLockBodyScroll custom hook to prevent page scrolling when the menu is open. The menu itself is scrollable, but the page scroll remains locked to enhance the user experience. This approach ensures that users aren't distracted by two conflicting scroll behaviors when the menu is visible, creating a smoother and more intuitive interaction.

Code implementation
1const menuItems = loadData(20);
2
3const Menu: FC = () => {
4 // Call the hook to prevent page scrolling
5 // when the component is mounted
6 useLockBodyScroll();
7
8 const handleClick = () => {};
9
10 return (
11 <div className="absolute -top-2 left-14 w-40 p-2">
12 <List
13 items={menuItems}
14 scrollable
15 onClick={handleClick}
16 />
17 </div>
18 );
19};
20
21const KEY_CLOSE = 'Escape';
22
23const Container: FC = () => {
24 // State to track whether the menu is open or not
25 const [isOpen, setIsOpen] = useState(false);
26
27 // Function to handle button click to toggle menu visibility
28 const handleToggle = () => {
29 // Toggle the `isOpen` state to the opposite value
30 setIsOpen((prev) => !prev);
31 };
32
33 // Function to handle closing the menu
34 const handleClose = () => {
35 setIsOpen(false);
36 };
37
38 // Call the `useKeyPress` hook to handle the `Escape` key press and close the modal
39 useKeyPress(KEY_CLOSE, handleClose);
40
41 return (
42 <Layer title="Container">
43 <div className="flex items-center justify-center">
44 <div className="relative w-14">
45 <button
46 onClick={handleToggle}
47 className="rlu__focus relative flex h-14 w-14 items-center justify-center rounded-xl bg-blue-500 text-white"
48 >
49 {/* Render a hamburger or close icon based on `isOpen` state */}
50 {isOpen ? <IconClose /> : <IconOpen />}
51 </button>
52 {/* Conditional rendering of menu based on `isOpen` state */}
53 {isOpen && <Menu />}
54 </div>
55 </div>
56 <Message>
57 <span>
58 After opening the menu, you can press the following key on
59 your keyboard to close it:
60 </span>
61 <Kbd id={KEY_CLOSE} />
62 </Message>
63 </Layer>
64 );
65};
66
An interactive output of the code above

Modal with Body Scroll Lock

This example demonstrates the use of the useLockBodyScroll custom hook alongside a modal component. The hook is applied to prevent page scrolling when the modal is open, ensuring a more focused and uninterrupted experience for the user.

Code implementation
1interface ModalProps {
2 onClose: () => void;
3}
4
5const KEY_CLOSE = 'Escape';
6
7const ModalWrapper: FC<ModalProps> = ({ onClose: handleClose }) => {
8 // Call the `useLockBodyScroll` hook to prevent page scrolling when the component is mounted
9 useLockBodyScroll();
10
11 // Call the `useKeyPress` hook to handle the `Escape` key press and close the modal
12 useKeyPress(KEY_CLOSE, handleClose);
13
14 return (
15 <Modal>
16 <Message>
17 <span>
18 Press the following key on your keyboard to close the modal
19 window:
20 </span>
21 <Kbd id={KEY_CLOSE} />
22 <span>Or click the following button:</span>
23 </Message>
24 <Button onClick={handleClose}>Close Modal</Button>
25 </Modal>
26 );
27};
28
29const Container: FC = () => {
30 // State to track whether the modal is open or not
31 const [isOpen, setIsOpen] = useState(false);
32
33 // Function to handle closing the modal
34 const handleClose = () => {
35 setIsOpen(false);
36 };
37
38 // Function to handle opening the modal
39 const handleOpen = () => {
40 setIsOpen(true);
41 };
42
43 return (
44 <Layer title="Container">
45 <div className="flex w-full justify-center">
46 <Button onClick={handleOpen}>Open Modal</Button>
47 </div>
48 {/* Conditional rendering of modal based on `isOpen` state */}
49 {isOpen && <ModalWrapper onClose={handleClose} />}
50 </Layer>
51 );
52};
53
An interactive output of the code above
Last updated on by @skrykateSuggest an improvement on Github repository.