React Burger Menu: The Complete Guide to Slide-Out Menus, Animations & Customization
React Burger Menu: The Complete Guide to Slide-Out Menus, Animations & Customization
⏱ Estimated reading time: 12 minutes | Difficulty: Beginner–Intermediate
react-burger-menu
React sidebar menu
React mobile navigation
React animations
There’s a moment in every React developer’s life when a product designer drops a Figma file on your desk
featuring a gorgeous animated slide-out menu — and then smiles and walks away. You stare at it. It has
a smooth push transition, a stacked navigation panel, an overlay, and a custom close button. You briefly
consider faking a medical emergency. Then you remember:
react-burger-menu exists.
react-burger-menu
is a battle-tested React library that gives you over a dozen out-of-the-box CSS-powered
animated navigation menus — from simple slides to elastic, push-rotate, and bubble
effects — with minimal configuration and full style control. It handles the heavy lifting of
React mobile menu behavior, overlay management, keyboard accessibility, and
animation state, so you don’t have to reinvent the off-canvas wheel every project.
This guide walks you through everything: installation, setup, animation variants, customization,
integration with React Router, and production-level styling patterns. Whether you’re prototyping
a React sidebar menu for a dashboard or building a fully accessible
React mobile navigation for a content-heavy site, you’ll find practical answers here —
not vague hand-waving about « just passing props. »
What Is react-burger-menu and Why Should You Use It?
At its core, react-burger-menu is a React navigation component library authored by
Imogen Wentworth (GitHub: negomi). It abstracts the notoriously fiddly parts of building a
React slide-out menu: CSS transition management, body-scroll locking when the menu
is open, overlay click-to-close behavior, and cross-browser animation compatibility. The library
ships with 15 distinct animation styles, each implemented as a named React component.
The value proposition is straightforward: instead of spending three hours writing custom keyframe
animations and debugging z-index stacking in Safari, you import slide or push
from the package, drop your nav links inside it, and get a professional-grade
animated navigation in about 20 lines of code. The library doesn’t impose a design
system on you — it’s intentionally style-agnostic, meaning you bring the colors, fonts, and
layout; it brings the motion.
One important thing to set expectations on: react-burger-menu is a controlled component. You manage
the open/closed state via React hooks or class-based state. This is actually a feature, not a
limitation — it means the menu integrates cleanly with your existing state management, whether that’s
local useState, Redux, or Zustand. The library won’t fight you for control of your UI.
Installation: Getting react-burger-menu Into Your Project
react-burger-menu installation is a one-liner. Open your terminal in the project root and run:
# npm
npm install react-burger-menu
# yarn
yarn add react-burger-menu
# pnpm
pnpm add react-burger-menu
The package has a peer dependency on React and ReactDOM (version 16.3 or higher). It works cleanly
with React 17 and React 18. If you’re on React 18 with concurrent features, the library integrates
without issues — it doesn’t use legacy lifecycle methods or deprecated APIs. No additional Babel
plugins, PostCSS configuration, or CSS imports are required out of the box.
After installation, you’ll import whichever animation style you want directly from the package.
There’s no default export — every animation is a named export. This keeps your bundle size lean:
tree-shaking will strip out the animation styles you don’t use. If you’re using Create React App,
Next.js, Vite, or any modern build tool with proper ES module support, tree-shaking happens automatically.
(
window, document). If you’re using Next.js with SSR, import thecomponent dynamically with
ssr: false to avoid hydration errors:
const Menu = dynamic(() => import('./BurgerMenu'), { ssr: false });
Basic Setup: Your First React Slide-Out Menu
Once the package is installed, react-burger-menu setup follows a predictable pattern.
You import the animation component, wrap your navigation links inside it, and manage the open state
with a useState hook. Here’s a minimal but fully functional example:
import React, { useState } from 'react';
import { slide as Menu } from 'react-burger-menu';
const BurgerNav = () => {
const [isOpen, setIsOpen] = useState(false);
const handleStateChange = (state) => {
setIsOpen(state.isOpen);
};
const closeMenu = () => setIsOpen(false);
return (
<Menu
isOpen={isOpen}
onStateChange={handleStateChange}
pageWrapId={"page-wrap"}
outerContainerId={"outer-container"}
>
<a href="/" onClick={closeMenu}>Home</a>
<a href="/about" onClick={closeMenu}>About</a>
<a href="/services" onClick={closeMenu}>Services</a>
<a href="/contact" onClick={closeMenu}>Contact</a>
</Menu>
);
};
export default BurgerNav;
Two props deserve special attention: pageWrapId and outerContainerId.
These are required by animation types that physically push or reveal the page content (like
push, reveal, or pushRotate). They tell the library which
DOM element wraps your main content and which element is the outermost container, so it can apply
the correct CSS transforms. For slide-only animations, these props are optional — but it’s good
practice to always include them.
The onStateChange callback is how the library notifies your component when the menu
state changes from outside — for example, when the user clicks the overlay to close it. Without
this handler, your React state and the library’s internal state can drift out of sync, causing
the classic bug where the menu is visually closed but your state says it’s open. Always sync
state through this callback.
Your corresponding HTML structure should look like this, with proper IDs in place:
// index.js or App.jsx
<div id="outer-container">
<BurgerNav />
<main id="page-wrap">
{/* your page content */}
</main>
</div>
React Burger Menu Animation Types: All 15 Effects Explained
The headline feature of react-burger-menu is its animation library. Each effect is available as a
named import and produces a distinct React menu animation behavior. You don’t
configure animations through props — you simply swap the import. Change slide to
elastic, save the file, and you have a completely different motion feel. Here’s a
breakdown of the most popular options:
- slide — The menu slides in from the left over the page content. Clean, simple, universal.
- push — The menu slides in and simultaneously pushes the page content to the right. Requires
pageWrapId. - pushRotate — Like push, but the page content also rotates slightly as it moves. Adds a 3D feel.
- reveal — The menu is stationary underneath; the page content slides away to reveal it.
- stack — Menu items stack into view sequentially, creating a staggered entrance effect.
- elastic — The menu bounces in with an elastic easing function. Playful and modern.
- bubble — The menu expands from a circular origin point, like a material design ripple.
- fallDown — Menu items fall into place from above, one after another.
- scaleDown — The page scales down while the menu appears. Dramatic and immersive.
- scaleRotate — Combines scale and rotation of the page for a layered depth effect.
Switching between them is literally a one-word change in your import statement:
// Slide effect
import { slide as Menu } from 'react-burger-menu';
// Push effect
import { push as Menu } from 'react-burger-menu';
// Elastic effect
import { elastic as Menu } from 'react-burger-menu';
// Bubble effect
import { bubble as Menu } from 'react-burger-menu';
From a UX perspective, choose your animation based on context. The slide and
push effects are safest for corporate or utility applications — they’re predictable
and don’t surprise users. elastic and bubble work well for consumer apps
and creative portfolios where personality matters. Avoid scaleDown and
pushRotate on content-heavy pages with lots of text — the 3D transforms can cause
readability issues mid-animation on lower-end mobile devices.
Customizing react-burger-menu: Styles, Colors, and Layout
Out of the box, react-burger-menu renders with default styles — a dark sidebar, white hamburger icon,
and basic overlay. Useful for prototyping, not acceptable for production. The library exposes a
styles prop that accepts a plain JavaScript object with camelCased keys corresponding
to specific parts of the menu. This is the primary mechanism for
react-burger-menu customization.
const menuStyles = {
bmBurgerButton: {
position: 'fixed',
width: '36px',
height: '30px',
left: '36px',
top: '36px',
},
bmBurgerBars: {
background: '#373a47',
borderRadius: '3px',
},
bmBurgerBarsHover: {
background: '#a90000',
},
bmCrossButton: {
height: '24px',
width: '24px',
},
bmCross: {
background: '#bdc3c7',
},
bmMenuWrap: {
position: 'fixed',
height: '100%',
top: '0',
},
bmMenu: {
background: '#373a47',
padding: '2.5em 1.5em 0',
fontSize: '1.15em',
},
bmMorphShape: {
fill: '#373a47',
},
bmItemList: {
color: '#b8b7ad',
padding: '0.8em',
},
bmItem: {
display: 'inline-block',
color: '#ffffff',
marginBottom: '16px',
textDecoration: 'none',
transition: 'color 0.2s ease',
},
bmOverlay: {
background: 'rgba(0, 0, 0, 0.3)',
},
};
// Then pass it to your Menu component:
<Menu styles={menuStyles}>
{/* nav items */}
</Menu>
Beyond the styles prop, you can also use the react-burger-menu styles
via CSS class names. The library adds predictable BEM-style class names to all elements
(.bm-menu, .bm-burger-button, .bm-overlay, etc.),
so you can target them from an external stylesheet or CSS module. The inline styles
prop takes precedence over stylesheet rules, so you can use either or both approaches depending
on your project’s styling architecture.
For teams using styled-components or Emotion, the recommended pattern is to target the BEM class
names from within a styled wrapper component. This keeps your component tree clean and your
styles co-located with the component logic. If you’re on Tailwind CSS, the class-name approach
works cleanly — though you’ll need to use !important modifiers for some properties
due to specificity conflicts with Tailwind’s reset styles.
Integrating with React Router and Programmatic Navigation
A common real-world requirement is using react-burger-menu as a React sidebar menu
within a React Router setup — where clicking a nav link should close the menu and navigate to a
new route simultaneously. This requires a small but important pattern: close the menu state
explicitly on link click, then let React Router handle the navigation.
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { slide as Menu } from 'react-burger-menu';
const Navigation = () => {
const [menuOpen, setMenuOpen] = useState(false);
const navigate = useNavigate();
const handleNavClick = (path) => {
setMenuOpen(false);
navigate(path);
};
return (
<Menu
isOpen={menuOpen}
onStateChange={({ isOpen }) => setMenuOpen(isOpen)}
pageWrapId={"page-wrap"}
outerContainerId={"outer-container"}
>
<button onClick={() => handleNavClick('/')}>Home</button>
<button onClick={() => handleNavClick('/about')}>About</button>
<button onClick={() => handleNavClick('/projects')}>Projects</button>
</Menu>
);
};
Note the use of button elements rather than anchor tags inside the menu. When you’re
using React Router’s programmatic navigation via useNavigate, rendering <a>
elements that also fire a navigate() call can cause double-navigation or unexpected
scroll behavior. Buttons are semantically neutral, accessible by keyboard, and don’t carry
anchor-specific browser defaults that might interfere.
If you prefer to keep <Link> components for semantic and accessibility reasons,
use the onClick handler on the Link itself to close the menu. React Router will handle
the navigation, and your state update will close the menu concurrently. Both patterns work reliably —
choose based on your team’s conventions and accessibility requirements.
Building a Production-Ready React Mobile Menu
Using react-burger-menu purely as a visible-on-all-screens component is an antipattern.
On desktop, a persistent sidebar or a standard horizontal navbar is almost always superior in
usability to a hamburger menu. The right approach is to use react-burger-menu as your
React mobile navigation solution and conditionally render it based on viewport width.
import React, { useState, useEffect } from 'react';
import { slide as Menu } from 'react-burger-menu';
import DesktopNav from './DesktopNav';
const ResponsiveNav = () => {
const [isMobile, setIsMobile] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
useEffect(() => {
const checkMobile = () => setIsMobile(window.innerWidth < 768);
checkMobile();
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}, []);
if (!isMobile) return <DesktopNav />;
return (
<Menu
isOpen={menuOpen}
onStateChange={({ isOpen }) => setMenuOpen(isOpen)}
pageWrapId={"page-wrap"}
outerContainerId={"outer-container"}
>
<a href="/">Home</a>
<a href="/work">Work</a>
<a href="/contact">Contact</a>
</Menu>
);
};
For a more robust solution — one that avoids layout flashes on initial load — use a CSS-based
approach alongside the conditional rendering. Apply a utility class to the burger button wrapper
that hides it above a certain breakpoint (display: none at min-width: 768px),
and always render the <DesktopNav> as a visually hidden element on mobile. This
prevents the JavaScript-dependent conditional from causing content layout shifts (CLS), which
directly impact your Core Web Vitals score.
Accessibility is non-negotiable in production. react-burger-menu handles basic ARIA attributes
(the hamburger button gets aria-expanded and aria-label), but you
should verify these with a screen reader. Add a disableAutoFocus prop if you’re
managing focus manually, and ensure your nav links inside the menu are reachable by keyboard
tab order when the menu is open. Testing with VoiceOver on iOS and NVDA on Windows before
shipping is a minimum bar.
Advanced Patterns: Context, State Management, and Custom Burger Icons
In larger applications, you’ll often need to control the menu state from multiple places — a nav
button in the header, a « close » button inside the menu panel, and possibly a keyboard shortcut.
The cleanest pattern is a React Context provider that wraps your application
and exposes the isOpen state and a toggleMenu function:
import React, { createContext, useContext, useState } from 'react';
const MenuContext = createContext();
export const MenuProvider = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => setIsOpen(prev => !prev);
const closeMenu = () => setIsOpen(false);
const openMenu = () => setIsOpen(true);
return (
<MenuContext.Provider value={{ isOpen, toggleMenu, closeMenu, openMenu }}>
{children}
</MenuContext.Provider>
);
};
export const useMenu = () => useContext(MenuContext);
With this pattern, your <BurgerNav> component consumes useMenu()
to get isOpen and closeMenu, and any other component in the tree can
call openMenu() or toggleMenu() without prop drilling. It’s a clean,
scalable architecture that works equally well with Redux (dispatch an action) or Zustand (update a slice).
For custom burger icon styling, react-burger-menu offers a customBurgerIcon and
customCrossIcon prop. You can pass any React element — an SVG, an icon from
a design system, or a fully animated component:
<Menu
customBurgerIcon={<img src="/icons/menu.svg" alt="Open menu" />}
customCrossIcon={<img src="/icons/close.svg" alt="Close menu" />}
>
{/* nav links */}
</Menu>
One gotcha worth knowing: if you pass a custom burger icon, the library still positions it based
on the bmBurgerButton style key. Make sure your icon dimensions match what you’ve
set in the styles object, or you’ll get misaligned click targets — which is both a visual bug
and an accessibility failure (WCAG 2.5.5 recommends minimum 44×44px touch targets).
Performance Considerations and Common Pitfalls
react-burger-menu is lightweight, but there are a few patterns that can introduce unnecessary
re-renders or layout performance issues. The biggest culprit is passing an inline object literal
as the styles prop. Every render creates a new object reference, which triggers
a re-render of the Menu component even when nothing has changed. Define your styles object
outside the component or memoize it with useMemo:
// ❌ Causes unnecessary re-renders
const MyNav = () => (
<Menu styles={{ bmMenu: { background: '#333' } }}>...</Menu>
);
// ✅ Stable reference
const menuStyles = { bmMenu: { background: '#333' } };
const MyNav = () => (
<Menu styles={menuStyles}>...</Menu>
);
Another common issue arises with the push, reveal, and
pushRotate animations when page content contains position: fixed
elements (sticky headers, floating action buttons, cookie banners). These elements don’t
transform with the page wrapper, causing them to overlap the menu panel. The fix: apply
the same CSS transform to fixed-position elements when the menu is open, or switch to a
slide animation that doesn’t move the page content.
Finally, watch out for the body-scroll-lock behavior on iOS. react-burger-menu prevents body
scrolling when the menu is open, but on older iOS versions (14 and below), the
overflow: hidden approach doesn’t fully prevent scroll on the body. If your users
are on iOS and scrolling through the overlay is a reported issue, consider pairing the library
with body-scroll-lock (npm package) for more reliable mobile scroll prevention.
Call disableBodyScroll(menuElement) on open and enableBodyScroll(menuElement)
on close, triggered via onStateChange.
Frequently Asked Questions
Q: How do I install and set up react-burger-menu in a React project?
Run npm install react-burger-menu (or yarn add react-burger-menu) in
your project root. Then import the animation style you want — for example,
import { slide as Menu } from 'react-burger-menu' — and wrap your navigation
links inside the <Menu> component. Control the open/close state with a
useState hook and sync it via the onStateChange callback. Set
pageWrapId and outerContainerId on the component matching your
HTML structure to enable push-type animations.
Q: How do I customize the styles in react-burger-menu?
Pass a styles prop object to the Menu component using the library’s reserved keys:
bmBurgerButton, bmBurgerBars, bmMenu,
bmOverlay, bmItem, and others. Each key corresponds to a specific
DOM element rendered by the library. Alternatively, target the auto-generated BEM class names
(.bm-menu, .bm-overlay, etc.) in your CSS or CSS modules. Inline
styles via the styles prop take priority over external stylesheets.
Q: What animation types does react-burger-menu support?
react-burger-menu includes 15 CSS-powered animation styles: slide,
push, pushRotate, reveal,
stack, elastic, bubble,
fallDown, scaleDown, scaleRotate,
and several more. Each is a named export from the package. Switching animations requires
only changing the import — no additional configuration. Animations that move the page content
(push, reveal, pushRotate) require pageWrapId and outerContainerId
props to function correctly.