SideBar multi level navigation in React with material UI

Ratheesh Kumar
4 min readJan 2, 2020

--

In this article we are going to see how we can make sidebar menu using react hooks and material UI. Below is how it looks once it is done.

Before diving into details if you want to see this in action, then the source code is available in this repo — https://github.com/ratheeshkm/multilevel-menu-react-materialui.git .

git clone https://github.com/ratheeshkm/multilevel-menu-react-materialui.git
cd multilevel-menu-react-materialui.git
yarn install
yarn start

Lets check little more details, we will be using Facebook own create-react-app tool.

yarn create react-app side-bar && cd side-bar

Now we have to install dependency libraries

yarn add @material-ui/core @material-ui/styles @material-ui/icons 

Each menu items has been used for navigation so we have to use react-route-dom libray.

yarn add react-router-dom

For little clean up, we can delete couple of existing files

rm src/App.css src/App.test.js src/serviceWorker.js src/setupTests.js src/index.css

Create a sideBarItems.js file for keeping all our menu details like name, url etc in src directory.

touch src/sideBarItems.js

Use below data in sideBarItems.js

export default {
"data" : [
{
"name": "Item1",
"url": "/item1"
},
{
"name": "Item2",
"url": "/item2"
},
{
"name": "Item3",
"children": [
{
"name": "Child31",
"url": "/child31"
},
{
"name": "Child32",
"url": "/child32"
},
{
"name": "Child33",
"url": "/child33"
}
]
},
{
"name": "Item4",
"children": [
{
"name": "Child41",
"url": "/child41"
},
{
"name": "Child42",
"url": "/child42"
},
{
"name": "Child43",
"children": [
{
"name": "Child431",
"url": "/child431"
},
{
"name": "Child432",
"url": "/child432,"
},
{
"name": "Child433",
"url": "/child433"
}
]
}
]
}
]
}

Inside the src/ folder create a file named as MenuBar.js with the below code which is used for displaying the side bar menu. All the styles used by this component is available in menuBarStyles.js which you can edit/replace according to your style needs.

touch src/MenuBar.js src/menuBarStyles.js

MenuBar.js

import React, {  useState, forwardRef } from 'react';
import {List, ListItem, Collapse, Button, Drawer } from '@material-ui/core';
import clsx from 'clsx';
import { ExpandLess, ExpandMore } from "@material-ui/icons";
import menuItems from './sideBarItems';
import { NavLink as RouterLink } from 'react-router-dom';
import useStyles from './menuBarStyles';
const MenuBar = (props) => {
const [ menu, setMenu ] = useState({});
const { className, ...rest } = props;
const classes = useStyles();
const handleClick = (item) => {
let newData = {...menu, [item] : !menu[item]};
setMenu(newData);
}
const CustomRouterLink = forwardRef((props, ref) => (
<div ref={ref} style={{ flexGrow: 1 }}>
<RouterLink {...props} />
</div>
));
const handleMenu = ( children, level=0 ) => {
return children.map(({children, name, url, links }) => {
if ( !children ) {
return (
<List component="div" disablePadding key={ name }>
<ListItem
className={classes.item}
disableGutters
style={{padding:"0px"}}
key={name}
>
<Button
className={clsx({
[classes.btnRoot] : true,
[classes.button] : true,
[classes.subMenu] : level
})}
component={CustomRouterLink}
to={url}
>
{name}
</Button>
</ListItem>
</List>
)
}
return (
<div key={ name }>
<ListItem
className={classes.item}
disableGutters
key={name}
onClick={() => handleClick(name)}
>
<Button
className={clsx({
[classes.btnRoot] : true,
[classes.button] : true,
[classes.subMenu] : level
})}>
{ name } { menu[ name ] ? <ExpandLess /> : <ExpandMore />}
</Button>
</ListItem>
<Collapse
in={ (menu[name]) ? true : false }
timeout="auto"
unmountOnExit
>
{ handleMenu( children, 1) }
</Collapse>
</div>
)
})
}
return (
<Drawer
anchor="left"
classes={{ paper: classes.drawer }}
open={true}
variant="persistent"
>
<List {...rest} className={clsx(classes.root, className)} >
{ handleMenu(menuItems.data) }
</List>
</Drawer>
)
}
export default MenuBar;

In this component, if the menu items doesn’t contain a sub menu then just render the menu item else it using recursive method to render sub menus.

menuBarStyle.js

import { makeStyles } from '@material-ui/styles';
import { colors } from '@material-ui/core';
const useStyles = makeStyles(theme => ({
root: {
justifyContent : "left",
},
drawer : {
paddingTop : "20px",
width: "250px",
},
item: {
display: 'flex',
paddingTop: 0,
paddingBottom: 0,
},
button: {
color: colors.blueGrey[800],
padding: '10px 8px',
justifyContent: 'flex-start',
textTransform: 'none',
letterSpacing: 0,
width: '100%',
},
btnRoot : {
paddingLeft : "25px",
justifyContent : "left !important"
},
subMenu : {
paddingLeft : "50px !important",
}
}));
export default useStyles;

Load the MenuBar component in App.js

App.js

import React, { Component } from 'react';
import MenuBar from './MenuBar'
import { BrowserRouter } from 'react-router-dom'
class App extends Component {
render() {
return (
<BrowserRouter>
<MenuBar/>
</BrowserRouter>
)
}
}
export default App;

index.js

Updated the index.js file to remove the files which are not required with this task.

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));

This is a basic component implementation and you can add or update more functionalities, styles according your needs.

Thanks.

--

--