Mastering Mobile Navigation Menus in React
The Challenge of Full-Page Menus
When designing mobile navigation menus, we often encounter a common problem: how to prevent scrolling on the underlying page while keeping the menu open. One solution is to add position: fixed
to the original page, but this introduces another issue – losing the current scroll position when closing the menu.
A Custom Hook to the Rescue
To overcome these challenges, we’ll create a custom Hook in a React application that allows us to stop the page from scrolling when a full-page mobile menu is open, while maintaining the scroll position when the menu is closed.
Setting Up the Frontend
First, let’s create a new React application using create-react-app
. Once set up, we can build a simple frontend to demonstrate our custom Hook.
The Code
Here’s the code for our App.js
file:
“`
//./src/App.js
import React, { useState, useEffect } from ‘eact’;
function App() {
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
const handleScroll = () => {
setScrollPosition(window.scrollY);
};
window.addEventListener(‘scroll’, handleScroll);
return () => {
window.removeEventListener(‘scroll’, handleScroll);
};
}, []);
return (
Mobile Navigation Menu
Current scroll position: {scrollPosition}
);
}
export default App;
And here's the styling for our `App.js` file:
/*./src/App.css */
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
“`
Creating the Mobile Navigation Menu
Next, let’s create a Nav.js
component to house our mobile navigation menu:
“`
//./src/components/Nav.js
import React from ‘eact’;
function Nav() {
return (
);
}
export default Nav;
“
useMenuControl` Custom Hook**
**The
Now, let’s create our custom Hook, useMenuControl
, to control the full-page mobile menu opening and closing, as well as maintaining the scroll position:
“`
//./src/hooks/useMenuControl.js
import { useState, useEffect } from ‘eact’;
function useMenuControl() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [originalOverflow, setOriginalOverflow] = useState(”);
useEffect(() => {
if (isMenuOpen) {
setOriginalOverflow(document.body.style.overflow);
document.body.style.overflow = ‘hidden’;
} else {
document.body.style.overflow = originalOverflow;
}
}, [isMenuOpen]);
const clickHandler = () => {
setIsMenuOpen(!isMenuOpen);
};
return [isMenuOpen, clickHandler];
}
export default useMenuControl;
“`
Implementing the Custom Hook
Let’s revisit our Nav
component and add the useMenuControl
custom Hook:
“`
//./src/components/Nav.js
import React from ‘eact’;
import useMenuControl from ‘../hooks/useMenuControl’;
function Nav() {
const [isMenuOpen, clickHandler] = useMenuControl();
return (
);
}
export default Nav;
And finally, let's add some styles for the `.menuOpen` class:
/*./src/components/Nav.css */
.menuOpen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background-color: #fff;
z-index: 1;
}
“`
The Final Result
With these pieces in place, we now have a React application that allows us to open a full-page mobile menu, maintaining the scroll position on the page underneath, while preventing scrolling on the page while the menu is open.
Check out the final version of this application on GitHub, and follow me on Twitter for more helpful tips and content on the JavaScript ecosystem and web development.