react-burger-menu, react-navigation-drawer, and react-sidebar are all React libraries designed to create off-canvas navigation menus (often called "drawers" or "sidebars"), but they target fundamentally different platforms and architectural needs. react-burger-menu is a highly customizable, animation-focused library for standard web applications, offering multiple menu styles (slide, push, overlay) without enforcing a specific layout structure. react-navigation-drawer is a native-component wrapper specifically built for React Native mobile apps, integrating deeply with the react-navigation ecosystem to provide gesture-based interactions and native performance. react-sidebar is a lightweight, opinionated solution for web apps that prioritizes simplicity and content pushing, often used when developers need a quick, standard sidebar implementation without complex animation configurations.
Building off-canvas navigation is a common requirement, but the "right" tool depends entirely on your target platform (Web vs. Mobile) and your need for customization versus convention. react-burger-menu, react-navigation-drawer, and react-sidebar solve similar visual problems but operate in different domains with distinct architectural implications.
The most critical decision factor is your deployment target. Mixing these up leads to immediate build failures or broken interactions.
react-navigation-drawer is built strictly for React Native. It relies on native gesture handlers and native view primitives. You cannot use this in a standard React DOM (web) project.
// react-navigation-drawer: React Native only
import { createDrawerNavigator } from '@react-navigation/drawer';
const Drawer = createDrawerNavigator();
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={HomeScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
}
react-burger-menu and react-sidebar are built for React DOM (Web). They manipulate CSS transforms and DOM nodes to achieve the slide effect. They will not work in a pure React Native environment without complex web-view wrappers.
// react-burger-menu: Web only
import { slide as Menu } from 'react-burger-menu';
function WebMenu() {
return (
<Menu>
<a id="home" href="/">Home</a>
<a id="about" href="/about">About</a>
</Menu>
);
}
// react-sidebar: Web only
import Sidebar from 'react-sidebar';
function WebSidebar() {
return (
<Sidebar sidebar={<MySidebarContent />} docked={false}>
<MainContent />
</Sidebar>
);
}
How the menu interacts with the main content defines the user experience. These libraries offer different levels of control over this physics.
react-burger-menu provides the deepest control over animation modes. You can choose between slide (overlay), push (shifting content), reveal (clipping content), and more via the customBurgerMenu or standard props.
// react-burger-menu: Explicitly choosing 'push' animation
<Menu mode="push" width={300}>
<a href="/">Link</a>
</Menu>
react-sidebar focuses primarily on the overlay and push paradigms but abstracts the complex math. It uses the docked prop to change behavior, but fine-tuning the transition curve requires overriding internal styles.
// react-sidebar: Toggling between docked (push-like) and undocked (overlay)
<Sidebar sidebar={<Comp />} docked={isDocked}>
<div>Main Content</div>
</Sidebar>
react-navigation-drawer uses native animations optimized for 60fps on mobile devices. The behavior is largely predefined to match iOS and Android standards (usually an overlay with a backdrop dim), though you can customize the drawer type.
// react-navigation-drawer: Configuring drawer type
<Drawer.Navigator
drawerType="back" // Options: 'front', 'back', 'slide'
screenOptions={{ drawerStyle: { width: 280 } }}
>
{/* screens */}
</Drawer.Navigator>
Mobile users expect swipes; desktop users expect clicks. The libraries handle these input methods differently.
react-navigation-drawer has built-in gesture recognition. Users can swipe from the edge of the screen to open the drawer without writing extra code. It also handles edge cases like interrupting animations mid-swipe.
// react-navigation-drawer: Gestures are enabled by default
// No extra code needed to support swipe-to-open
<Drawer.Navigator>
<Drawer.Screen name="Home" component={Home} />
</Drawer.Navigator>
react-burger-menu and react-sidebar do not include swipe gestures out of the box for web. You must implement touch event listeners yourself or use a companion library if swipe support is required on mobile web.
// react-burger-menu: Manual state control required for custom triggers
function CustomTrigger() {
const [menuOpen, setMenuOpen] = useState(false);
return (
<>
<button onClick={() => setMenuOpen(true)}>Open Menu</button>
<Menu isOpen={menuOpen} onClose={() => setMenuOpen(false)}>
<a href="/">Link</a>
</Menu>
</>
);
}
// react-sidebar: Requires manual state management for open/close
function CustomSidebar() {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<Sidebar
sidebar={<MySidebar />}
open={sidebarOpen}
onSetOpen={setSidebarOpen}
>
<button onClick={() => setSidebarOpen(true)}>Open</button>
<MainContent />
</Sidebar>
);
}
How the menu syncs with your application's URL and state varies significantly.
react-navigation-drawer is opinionated and integrated. It is part of the navigation state tree. Opening a drawer item automatically navigates and updates the history stack. It manages focus and back-button logic natively.
// react-navigation-drawer: Automatic navigation handling
<Drawer.Screen
name="Profile"
component={ProfileScreen}
options={{ title: 'User Profile' }}
/>
// Clicking this item automatically pushes the 'Profile' route
react-burger-menu and react-sidebar are unopinionated. They are purely UI components. You must manually listen for link clicks and close the menu, and you must handle URL updates using your own router (like react-router-dom).
// react-burger-menu: Manual close on click
<Menu onClose={() => setMenuOpen(false)}>
<a href="/about" onClick={() => setMenuOpen(false)}>About</a>
</Menu>
// react-sidebar: Manual close on click
<Sidebar onSetOpen={setOpen}>
<MySidebarContent onItemClick={() => setOpen(false)} />
</Sidebar>
Despite their platform differences, these libraries share core concepts in how they manage visibility and accessibility.
All three libraries support both controlled (you manage the state) and uncontrolled (internal state) patterns, though controlled is recommended for syncing with routing.
// All support controlled 'isOpen' / 'open' props
<Menu isOpen={state} /> // react-burger-menu
<Sidebar open={state} /> // react-sidebar
// react-navigation-drawer is mostly controlled by navigation state
Each library attempts to handle basic accessibility, such as trapping focus inside the menu when open and returning focus to the trigger button when closed.
// react-burger-menu: Auto-focus management
<Menu autoFocus={true} />
// react-sidebar: Focus handling via props
<Sidebar focusOnOpen={true} />
All allow deep customization of the visual appearance, though the method differs (CSS classes vs. style objects vs. theme configs).
// react-burger-menu: Custom CSS classes
<Menu styles={{ bmMenu: { background: '#373a47' } }} />
// react-sidebar: Style props
<Sidebar styles={{ sidebar: { background: '#fff' } }} />
// react-navigation-drawer: Theme configuration
<Drawer.Navigator screenOptions={{ drawerStyle: { backgroundColor: '#fff' } }} />
| Feature | react-burger-menu | react-navigation-drawer | react-sidebar |
|---|---|---|---|
| Platform | Web (React DOM) | Mobile (React Native) | Web (React DOM) |
| Primary Use | Highly custom web menus | Native mobile navigation | Simple web sidebars |
| Gestures | Manual implementation required | Built-in native swipe | Manual implementation required |
| Routing | Manual integration | Deeply integrated | Manual integration |
| Animation | Multiple modes (push, slide, reveal) | Native standard (overlay/slide) | Standard overlay/push |
| Complexity | High (many config options) | Medium (convention-based) | Low (simple API) |
react-navigation-drawer is the mandatory choice for React Native apps. It is not a matter of preference; it is the architectural standard for mobile navigation in the React ecosystem. Do not attempt to port web sidebar libraries to mobile unless you have a very specific, non-standard requirement.
For Web applications, the choice is between flexibility and simplicity:
Choose react-burger-menu if the menu is a central design element. If you need the content to "push" away, or if you want a "reveal" effect where the menu slides out from behind the content, this is the only library that supports those specific physics out of the box. It is ideal for creative portfolios or high-end marketing sites.
Choose react-sidebar if you need a functional utility. If you are building an admin dashboard or an internal tool and just need a standard drawer that overlays content without spending days tweaking animation curves, this library gets the job done with minimal code. It trades customization for speed of implementation.
Final Thought: Always match the tool to the platform first. Once on the web, decide if you are building an experience (react-burger-menu) or a tool (react-sidebar).
Choose react-burger-menu for web applications where you need full control over menu animations, distinct visual styles (like push vs. overlay), and accessibility features without being tied to a specific routing library. It is ideal for marketing sites, dashboards, or complex web UIs where the menu behavior is a key part of the brand experience.
Choose react-navigation-drawer exclusively for React Native mobile applications that already use the react-navigation stack. It is the only correct choice if you need native gesture handling (swipe to open), hardware back button support, and seamless integration with mobile navigation states.
Choose react-sidebar for standard web projects where you need a simple, no-frills sidebar that pushes content aside. It is best suited for internal tools, admin panels, or prototypes where development speed is prioritized over custom animation physics or complex transition effects.
An off-canvas sidebar React component with a collection of effects and styles using CSS transitions and SVG path animations.
Using Redux? Check out redux-burger-menu for easy integration of react-burger-menu into your project.
Live demo: negomi.github.io/react-burger-menu
To build the examples locally, first make sure you're using Node <11.0.0. Then run:
npm install
npm start
Then open localhost:8000 in a browser.
The test suite uses Mocha, Chai and Sinon, with jsdom.
To run the tests once, run:
npm test
To run them with a watcher, run:
npm run test:watch
The easiest way to use react-burger-menu is to install it from npm and include it in your own React build process (using Browserify, Webpack, etc).
You can also use the standalone build by including dist/react-burger-menu.js in your page. If you use this, make sure you have already included React, and it is available as a global variable.
Version 3.x uses Hooks, so if you're using React 16.8+:
npm install react-burger-menu --save
If you're using an earlier version of React:
npm install react-burger-menu@^2.9.2 --save
Items for the sidebar should be passed as child elements of the component using JSX.
import { slide as Menu } from 'react-burger-menu'
class Example extends React.Component {
showSettings (event) {
event.preventDefault();
.
.
.
}
render () {
// NOTE: You also need to provide styles, see https://github.com/negomi/react-burger-menu#styling
return (
<Menu>
<a id="home" className="menu-item" href="https://github.com/negomi/react-burger-menu/blob/HEAD//">Home</a>
<a id="about" className="menu-item" href="https://github.com/negomi/react-burger-menu/blob/HEAD//about">About</a>
<a id="contact" className="menu-item" href="https://github.com/negomi/react-burger-menu/blob/HEAD//contact">Contact</a>
<a onClick={ this.showSettings } className="menu-item--small" href="">Settings</a>
</Menu>
);
}
}
The example above imported slide which renders a menu that slides in on the page when the burger icon is clicked. To use a different animation you can substitute slide with any of the following (check out the demo to see the animations in action):
slidestackelasticbubblepushpushRotatescaleDownscaleRotatefallDownrevealSome animations require certain other elements to be on your page:
Page wrapper - an element wrapping the rest of the content on your page (except elements with fixed positioning - see the wiki for details), placed after the menu component
<Menu pageWrapId={ "page-wrap" } />
<main id="page-wrap">
.
.
.
</main>
Outer container - an element containing everything, including the menu component
<div id="outer-container">
<Menu pageWrapId={ "page-wrap" } outerContainerId={ "outer-container" } />
<main id="page-wrap">
.
.
.
</main>
</div>
If you are using an animation that requires either/both of these elements, you need to give the element an ID, and pass that ID to the menu component as the pageWrapId and outerContainerId props respectively.
Check this table to see which animations require these elements:
| Animation | pageWrapId | outerContainerId |
|---|---|---|
slide | ||
stack | ||
elastic | ✓ | ✓ |
bubble | ||
push | ✓ | ✓ |
pushRotate | ✓ | ✓ |
scaleDown | ✓ | ✓ |
scaleRotate | ✓ | ✓ |
fallDown | ✓ | ✓ |
reveal | ✓ | ✓ |
The menu opens from the left by default. To have it open from the right, use the right prop. It's just a boolean so you don't need to specify a value. Then set the position of the button using CSS.
<Menu right />
You can specify the width of the menu with the width prop. The default is 300.
<Menu width={ 280 } />
<Menu width={ '280px' } />
<Menu width={ '20%' } />
You can control whether the sidebar is open or closed with the isOpen prop. This is useful if you need to close the menu after a user clicks on an item in it, for example, or if you want to open the menu from some other button in addition to the standard burger icon. The default value is false.
// To render the menu open
<Menu isOpen />
<Menu isOpen={ true } />
// To render the menu closed
<Menu isOpen={ false } />
You can see a more detailed example of how to use isOpen here.
Note: If you want to render the menu open initially, you will need to set this property in your parent component's componentDidMount() function.
If you keep the menu state yourself it might be convenient to pass a custom function to be used when the user triggers something that should open the menu.
Called when:
<Menu onOpen={ handleOnOpen } />
Note: The menu will NOT open automatically if you pass this prop, so you must handle it yourself.
If you keep the menu state yourself it might be convenient to pass a custom function to be used when the user triggers something that should close the menu.
Called when:
<Menu onClose={ handleOnClose } />
Note: The menu will NOT close automatically if you pass this prop, so you must handle it yourself.
You can detect whether the sidebar is open or closed by passing a callback function to onStateChange. The callback will receive an object containing the new state as its first argument.
var isMenuOpen = function(state) {
return state.isOpen;
};
<Menu onStateChange={ isMenuOpen } />
By default, the menu will close when the Escape key is pressed. To disable this behavior, you can pass the disableCloseOnEsc prop. This is useful in cases where you want the menu to be open all the time, for example if you're implementing a responsive menu that behaves differently depending on the browser width.
<Menu disableCloseOnEsc />
keydown handlerFor more control over global keypress functionality, you can override the handler that this component sets for window.addEventListener('keydown', handler), and pass a custom function. This could be useful if you are using multiple instances of this component, for example, and want to implement functionality to ensure that a single press of the Escape key closes them all.
const closeAllMenusOnEsc = (e) => {
e = e || window.event;
if (e.key === 'Escape' || e.keyCode === 27) {
this.setState({areMenusOpen: false});
}
};
<MenuOne customOnKeyDown={closeAllMenusOnEsc} isOpen={areMenusOpen} />
<MenuTwo customOnKeyDown={closeAllMenusOnEsc} isOpen={areMenusOpen} />
Note: Using this prop will disable all the default 'close on Escape' functionality, so you will need to handle this (including determining which key was pressed) yourself.
You can turn off the default overlay with noOverlay.
<Menu noOverlay />
You can disable the overlay click event (i.e. prevent overlay clicks from closing the menu) with disableOverlayClick. This can either be a boolean, or a function that returns a boolean.
<Menu disableOverlayClick />
<Menu disableOverlayClick={() => shouldDisableOverlayClick()} />
You can disable all transitions/animations by passing noTransition.
<Menu noTransition />
This is useful if you want the menu to remain open across re-mounts, for example during SPA route changes.
You can replace the default bars that make up the burger and cross icons with custom ReactElements. Pass them as the customBurgerIcon and customCrossIcon props respectively.
<Menu customBurgerIcon={ <img src="https://raw.githubusercontent.com/negomi/react-burger-menu/HEAD/img/icon.svg" /> } />
<Menu customCrossIcon={ <img src="https://raw.githubusercontent.com/negomi/react-burger-menu/HEAD/img/cross.svg" /> } />
You should adjust their size using the .bm-burger-button and .bm-cross-button classes, but the element itself will have the class .bm-icon or .bm-cross if you need to access it directly.
You can also disable the icon elements so they won't be included at all, by passing false to these props.
<Menu customBurgerIcon={ false } />
<Menu customCrossIcon={ false } />
This can be useful if you want exclusive external control of the menu, using the isOpen prop.
There are optional id and className props, which will simply add an ID or custom className to the rendered menu's outermost element. This is not required for any functionality, but could be useful for things like styling with CSS modules.
<Menu id={ "sidebar" } className={ "my-menu" } />
You can also pass custom classNames to the other elements:
<Menu burgerButtonClassName={ "my-class" } />
<Menu burgerBarClassName={ "my-class" } />
<Menu crossButtonClassName={ "my-class" } />
<Menu crossClassName={ "my-class" } />
<Menu menuClassName={ "my-class" } />
<Menu morphShapeClassName={ "my-class" } />
<Menu itemListClassName={ "my-class" } />
<Menu overlayClassName={ "my-class" } />
And to the html and body elements (applied when the menu is open):
<Menu htmlClassName={ "my-class" } />
<Menu bodyClassName={ "my-class" } />
Note: Passing these props will prevent the menu from applying styles to the html or body elements automatically. See here for more explanation.
By default, the menu will set focus on the first item when opened. This is to help with keyboard navigation. If you don't want this functionality, you can pass the disableAutoFocus prop.
<Menu disableAutoFocus />
The menu's children are all wrapped in a nav element by default, as navigation is likely the most common use case for this component. However, it's a general purpose sidebar, so you can change this to a div if you're not using it for navigation:
<Menu itemListElement="div" />
All the animations are handled internally by the component. However, the visual styles (colors, fonts etc.) are not, and need to be supplied, either with CSS or with a JavaScript object passed as the styles prop.
The component has the following helper classes:
/* Position and sizing of burger button */
.bm-burger-button {
position: fixed;
width: 36px;
height: 30px;
left: 36px;
top: 36px;
}
/* Color/shape of burger icon bars */
.bm-burger-bars {
background: #373a47;
}
/* Color/shape of burger icon bars on hover*/
.bm-burger-bars-hover {
background: #a90000;
}
/* Position and sizing of clickable cross button */
.bm-cross-button {
height: 24px;
width: 24px;
}
/* Color/shape of close button cross */
.bm-cross {
background: #bdc3c7;
}
/*
Sidebar wrapper styles
Note: Beware of modifying this element as it can break the animations - you should not need to touch it in most cases
*/
.bm-menu-wrap {
position: fixed;
height: 100%;
}
/* General sidebar styles */
.bm-menu {
background: #373a47;
padding: 2.5em 1.5em 0;
font-size: 1.15em;
}
/* Morph shape necessary with bubble or elastic */
.bm-morph-shape {
fill: #373a47;
}
/* Wrapper for item list */
.bm-item-list {
color: #b8b7ad;
padding: 0.8em;
}
/* Individual item */
.bm-item {
display: inline-block;
}
/* Styling of overlay */
.bm-overlay {
background: rgba(0, 0, 0, 0.3);
}
The same styles can be written as a JavaScript object like this:
var styles = {
bmBurgerButton: {
position: 'fixed',
width: '36px',
height: '30px',
left: '36px',
top: '36px'
},
bmBurgerBars: {
background: '#373a47'
},
bmBurgerBarsHover: {
background: '#a90000'
},
bmCrossButton: {
height: '24px',
width: '24px'
},
bmCross: {
background: '#bdc3c7'
},
bmMenuWrap: {
position: 'fixed',
height: '100%'
},
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'
},
bmOverlay: {
background: 'rgba(0, 0, 0, 0.3)'
}
}
<Menu styles={ styles } />
Because this project uses CSS3 features, it's only meant for modern browsers. Some browsers currently fail to apply some of the animations correctly.
Chrome and Firefox have full support, but Safari and IE have strange behavior for some of the menus.
Check the FAQ (https://github.com/negomi/react-burger-menu/wiki/FAQ) to see if your question has been answered already, or open a new issue.
MIT