Building a navigation drawer with Material UI and React Router DOM
Tags: typescript, materialui, react
Published: 2020-01-22 14:44:43

---
title: Building a navigation drawer with Material UI and React Router DOM
published: true
description: How to create a performant and SEO-oriented application with Material UI and React
tags: typescript, materialui, react,
cover_image: https://thepracticaldev.s3.amazonaws.com/i/p1pyfsd7u8e82gy89equ.jpg
series: React and Material UI
---
In the last article, I built a basic `Drawer` that, at the end of this article, will contain a full working navigation block.

# Installing React Router DOM
At the root folder of `football-almanac`, I run

```bash
npm install react-router-dom @types/react-router-dom
```

It'll install [React Router DOM](https://reacttraining.com/react-router/web/guides/quick-start).

# Designing the navigation

First three URLs that come in mind for an application like this are
- `/` (the home page)
- `/standings`
- `/teams`

# Implementing React Router DOM

## Wrap everything!
To get started, I import `BrowserRouter` in `index.tsx` and I wrap the whole application within it.

```typescript
......
import { BrowserRouter } from 'react-router-dom';
......
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);
```
At this point, since the `App` component is going to grow too much, I split it into multiple components. This will help me to ensure a good level of isolation of them, also for testing purposes.

## The `Router` object

I find very helpful defining my routes as an object like this

```typescript
const Routes = [
  {
    path: [url],
    sidebarName: [label],
    icon: [material_ui_icon_name],
    component: [component_name],
  },
  ...
];
```

In this way, I can define my router once and reuse it when I need, as a module.

I define my routes in `Routes.tsx`.

```typescript
import React from 'react';

const Home: React.FC = () => {
  return (
    <h1>Home</h1>
  );
};

const Standings: React.FC = () => {
  return (
    <h1>Standings</h1>
  );
};

const Teams: React.FC = () => {
  return (
    <h1>Teams</h1>
  );
};

const Routes = [
  {
    path: '/',
    sidebarName: 'Home',
    component: Home
  },
  {
    path: '/standings',
    sidebarName: 'Standings',
    component: Standings
  },
  {
    path: '/teams',
    sidebarName: 'Teams',
    component: Teams
  },
];

export default Routes;
```

For the moment I create some placeholder components (`Home`, `Standings` and `Teams`).

## The `NavigationBar`

I create a new component subfolder, named `NavigationBar`. The new component is `NavigationBar.tsx`.

```typescript
import React, { useState } from 'react';

import { NavLink, withRouter } from 'react-router-dom';
import Routes from '../App/Routes';

import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import {
  AppBar,
  Toolbar,
  Typography,
  IconButton,
  Drawer,
  MenuList,
  MenuItem,
  ListItemText,
 } from '@material-ui/core';
import MenuIcon from '@material-ui/icons/Menu';

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
    title: {
      flexGrow: 1,
    },
    drawer: {
      width: 300,
    },
    fullList: {
      width: 'auto',
    },
  }),
);

const NavigationBar: React.FC = (props: any) => {
  const classes = useStyles();
  const [isOpen, setIsOpen] = useState(false);
  const toggleDrawer = (open: boolean) => (
    event: React.KeyboardEvent | React.MouseEvent,
  ) => {
    if (
      event.type === 'keydown' &&
      ((event as React.KeyboardEvent).key === 'Tab' ||
        (event as React.KeyboardEvent).key === 'Shift')
    ) {
      return;
    }

    setIsOpen(open);
  };

  const activeRoute = (routeName: any) => {
    return props.location.pathname === routeName ? true : false;
  }

  return (
    <div>
      <div className={classes.root}>
        <AppBar position="static">
          <Toolbar>
            <IconButton edge="start" className={classes.menuButton} color="inherit" aria-label="menu" onClick={toggleDrawer(true)}>
              <MenuIcon />
            </IconButton>
            <Typography variant="h6" className={classes.title}>
              Football Almanac
            </Typography>
          </Toolbar>
        </AppBar>
      </div>
      <Drawer classes={{ paper: classes.drawer }} open={isOpen} onClose={toggleDrawer(false)}>
        <div
          className={classes.fullList}
          role="presentation"
          onClick={toggleDrawer(false)}
          onKeyDown={toggleDrawer(false)}
        >
          <MenuList>
            {Routes.map((prop, key) => {
              return (
                <NavLink to={prop.path} style={{ textDecoration: 'none' }} key={key}>
                  <MenuItem selected={activeRoute(prop.path)}>
                    <ListItemText primary={prop.sidebarName} />
                  </MenuItem>
                </NavLink>
              );
            })}
          </MenuList>
        </div>
      </Drawer>
    </div>
  );
};

export default withRouter(NavigationBar);
```

To have the browser navigation available within this component, I used a **higher-order component** that comes with React Router DOM, `withRouter`. 
> It passes updated `match`, `location`, and `history` props to the wrapped component whenever it renders.

To learn more about `withRender`, take a look at the [documentation](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/withRouter.md).

## `App.tsx`

```typescript
import React from 'react';

import { Switch, Route } from 'react-router-dom';
import Routes from './Routes';

import NavigationBar from './NavigationBar/NavigationBar';

const App: React.FC = () => {
  return (
    <div>
      <NavigationBar />
      <Switch>
        {Routes.map((route: any) => (
          <Route exact path={route.path} key={route.path}>
            <route.component />
          </Route>
        ))}
      </Switch>
    </div>
  );
}

export default App;
```

Understanding the following snippet is essential: since we can easily add and remove routes from an independent module, it's enough iterating it and create a Route for each route defined in the object.

```typescript
<Switch>
  {Routes.map((route: any) => (
    <Route exact path={route.path} key={route.path}>
      <route.component />
    </Route>
  ))}
</Switch>
```

The result is the following

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/nbjiud7btwsc64zipa4o.gif)

# What's next
In the next step I will create the home page that will display some data fetched from the APIs. 

# Useful resources
- React Router DOM (Web and Native) https://reacttraining.com/react-router/
- `withRouter` https://reacttraining.com/react-router/web/api/withRouter
- Material UI `<MenuList>` https://material-ui.com/api/menu-list/