A well-designed, responsive navigation menu is crucial for any modern web application. In this guide, we'll walk through the process of creating a responsive navigation menu using React, covering various aspects and best practices.
A responsive navigation menu adapts to different screen sizes, ensuring a seamless user experience across devices. In this tutorial, we'll create a navigation menu that collapses into a hamburger menu on smaller screens and expands on larger ones.
First, let's set up a new React project using Create React App:
npx create-react-app responsive-nav-menu
cd responsive-nav-menu
npm start
Let's start by creating a basic navigation component. Create a new file called NavMenu.js
in the src
directory:
import React, { useState } from 'react';
const NavMenu = () => {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen(!isOpen);
};
return (
<nav className="nav-menu">
<div className="nav-logo">Logo</div>
<ul className={`nav-links ${isOpen ? 'active' : ''}`}>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#services">Services</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
<div className="nav-toggle" onClick={toggleMenu}>
<span></span>
<span></span>
<span></span>
</div>
</nav>
);
};
export default NavMenu;
This component creates a basic structure for our navigation menu, including a logo, navigation links, and a toggle button for mobile views.
Now, let's add some basic styles to our navigation menu. Create a new file called NavMenu.css
in the src
directory:
.nav-menu {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background-color: #333;
color: #fff;
}
.nav-logo {
font-size: 1.5rem;
font-weight: bold;
}
.nav-links {
display: flex;
list-style: none;
}
.nav-links li {
margin-left: 1rem;
}
.nav-links a {
color: #fff;
text-decoration: none;
}
.nav-toggle {
display: none;
flex-direction: column;
cursor: pointer;
}
.nav-toggle span {
width: 25px;
height: 3px;
background-color: #fff;
margin-bottom: 5px;
}
@media (max-width: 768px) {
.nav-links {
display: none;
}
.nav-links.active {
display: flex;
flex-direction: column;
position: absolute;
top: 60px;
left: 0;
right: 0;
background-color: #333;
}
.nav-links li {
margin: 1rem 0;
}
.nav-toggle {
display: flex;
}
}
Don't forget to import the CSS file in your NavMenu.js
:
import './NavMenu.css';
Our CSS already includes some responsive styles, but let's enhance it further by adding smooth transitions:
.nav-links {
display: flex;
list-style: none;
transition: all 0.3s ease-in-out;
}
@media (max-width: 768px) {
.nav-links {
transform: translateY(-100%);
opacity: 0;
pointer-events: none;
}
.nav-links.active {
transform: translateY(0);
opacity: 1;
pointer-events: auto;
}
}
Let's create a separate component for our hamburger menu icon. Create a new file called HamburgerIcon.js
:
import React from 'react';
const HamburgerIcon = ({ isOpen, toggleMenu }) => {
return (
<div className={`hamburger ${isOpen ? 'open' : ''}`} onClick={toggleMenu}>
<span></span>
<span></span>
<span></span>
</div>
);
};
export default HamburgerIcon;
Now, update the NavMenu.js
to use this new component:
import React, { useState } from 'react';
import HamburgerIcon from './HamburgerIcon';
import './NavMenu.css';
const NavMenu = () => {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen(!isOpen);
};
return (
<nav className="nav-menu">
<div className="nav-logo">Logo</div>
<ul className={`nav-links ${isOpen ? 'active' : ''}`}>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#services">Services</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
<HamburgerIcon isOpen={isOpen} toggleMenu={toggleMenu} />
</nav>
);
};
export default NavMenu;
Add some styles for the hamburger icon in NavMenu.css
:
.hamburger {
display: none;
flex-direction: column;
cursor: pointer;
}
.hamburger span {
width: 25px;
height: 3px;
background-color: #fff;
margin-bottom: 5px;
transition: all 0.3s ease-in-out;
}
.hamburger.open span:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
.hamburger.open span:nth-child(2) {
opacity: 0;
}
.hamburger.open span:nth-child(3) {
transform: rotate(-45deg) translate(7px, -6px);
}
@media (max-width: 768px) {
.hamburger {
display: flex;
}
}
To improve accessibility, let's add ARIA attributes and keyboard navigation:
import React, { useState, useRef, useEffect } from 'react';
import HamburgerIcon from './HamburgerIcon';
import './NavMenu.css';
const NavMenu = () => {
const [isOpen, setIsOpen] = useState(false);
const navRef = useRef(null);
const toggleMenu = () => {
setIsOpen(!isOpen);
};
useEffect(() => {
const handleOutsideClick = (event) => {
if (navRef.current && !navRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleOutsideClick);
return () => {
document.removeEventListener('mousedown', handleOutsideClick);
};
}, []);
const handleKeyPress = (event) => {
if (event.key === 'Enter' || event.key === ' ') {
toggleMenu();
}
};
return (
<nav className="nav-menu" ref={navRef}>
<div className="nav-logo">Logo</div>
<ul className={`nav-links ${isOpen ? 'active' : ''}`} role="menu">
<li role="none"><a href="#home" role="menuitem">Home</a></li>
<li role="none"><a href="#about" role="menuitem">About</a></li>
<li role="none"><a href="#services" role="menuitem">Services</a></li>
<li role="none"><a href="#contact" role="menuitem">Contact</a></li>
</ul>
<HamburgerIcon
isOpen={isOpen}
toggleMenu={toggleMenu}
onKeyPress={handleKeyPress}
aria-expanded={isOpen}
aria-label="Toggle menu"
/>
</nav>
);
};
export default NavMenu;
Update the HamburgerIcon.js
component:
import React from 'react';
const HamburgerIcon = ({ isOpen, toggleMenu, onKeyPress, ...props }) => {
return (
<div
className={`hamburger ${isOpen ? 'open' : ''}`}
onClick={toggleMenu}
onKeyPress={onKeyPress}
tabIndex="0"
role="button"
{...props}
>
<span></span>
<span></span>
<span></span>
</div>
);
};
export default HamburgerIcon;
To optimize performance, we can use React.memo to prevent unnecessary re-renders:
import React, { memo } from 'react';
const HamburgerIcon = memo(({ isOpen, toggleMenu, onKeyPress, ...props }) => {
return (
<div
className={`hamburger ${isOpen ? 'open' : ''}`}
onClick={toggleMenu}
onKeyPress={onKeyPress}
tabIndex="0"
role="button"
{...props}
>
<span></span>
<span></span>
<span></span>
</div>
);
});
export default HamburgerIcon;
Let's add a dropdown menu to our navigation:
import React, { useState, useRef, useEffect } from 'react';
import HamburgerIcon from './HamburgerIcon';
import './NavMenu.css';
const NavMenu = () => {
const [isOpen, setIsOpen] = useState(false);
const [dropdownOpen, setDropdownOpen] = useState(false);
const navRef = useRef(null);
const toggleMenu = () => {
setIsOpen(!isOpen);
};
const toggleDropdown = () => {
setDropdownOpen(!dropdownOpen);
};
useEffect(() => {
const handleOutsideClick = (event) => {
if (navRef.current && !navRef.current.contains(event.target)) {
setIsOpen(false);
setDropdownOpen(false);
}
};
document.addEventListener('mousedown', handleOutsideClick);
return () => {
document.removeEventListener('mousedown', handleOutsideClick);
};
}, []);
const handleKeyPress = (event) => {
if (event.key === 'Enter' || event.key === ' ') {
toggleMenu();
}
};
return (
<nav className="nav-menu" ref={navRef}>
<div className="nav-logo">Logo</div>
<ul className={`nav-links ${isOpen ? 'active' : ''}`} role="menu">
<li role="none"><a href="#home" role="menuitem">Home</a></li>
<li role="none"><a href="#about" role="menuitem">About</a></li>
<li role="none" className="dropdown">
<a href="#services" role="menuitem" onClick={toggleDropdown}>Services</a>
{dropdownOpen && (
<ul className="dropdown-menu" role="menu">
<li role="none"><a href="#service1" role="menuitem">Service 1</a></li>
<li role="none"><a href="#service2" role="menuitem">Service 2</a></li>
<li role="none"><a href="#service3" role="menuitem">Service 3</a></li>
</ul>
)}
</li>
<li role="none"><a href="#contact" role="menuitem">Contact</a></li>
</ul>
<HamburgerIcon
isOpen={isOpen}
toggleMenu={toggleMenu}
onKeyPress={handleKeyPress}
aria-expanded={isOpen}
aria-label="Toggle menu"
/>
</nav>
);
};
export default NavMenu;
Add styles for the dropdown menu in NavMenu.css
:
.dropdown {
position: relative;
}
.dropdown-menu {
display: none;
position: absolute;
top: 100%;
left: 0;
background-color: #444;
padding: 0.5rem 0;
list-style: none;
min-width: 150px;
}
.dropdown-menu li {
padding: 0.5rem 1rem;
}
.dropdown-menu a {
color: #fff;
text-decoration: none;
}
.dropdown:hover .dropdown-menu,
.dropdown-menu:hover {
display: block;
}
@media (max-width: 768px) {
.dropdown-menu {
position: static;
background-color: #555;
}
}
In this comprehensive guide, we've created a responsive navigation menu in React with various features including:
2024 © All rights reserved - buraxta.com