Create a Modern Dynamic Sidebar Menu in React Using Recursion
Tags: javascript, node, webdev, react
Published: 2019-11-17 03:19:59

Find me on [medium](https://medium.com/@jsmanifest)


In web pages, sidebars in web pages are amongst one of the most useful components that exist in the page due to their navigational functionality.

Today we will be building a modern sidebar in react using _recursion_. Recursion is a technique in which a function simply calls itself _repeatedly_ until a condition has been met. The [three rules of recursion](https://jsmanifest.com/recursion-inside-javascript/) applies when using recursion in this post:

1. The function should have a condition that self destructs itself
2. The function should have a base condition
3. The function should be calling itself

Sidebars are indeed essential to a web page, even if the level of its level of attention does not come first. This is because they can help users navigate in different ways, such as content that they may be interested in as opposed to a logical navigational menu.

But why would we even want to use recursion for sidebars? What difference does it make as opposed to writing out your sidebar items manually? If you've browsed through the internet for awhile, you might have come accross a website's sidebar and realized that some sidebar items have subsections. Some sites have sidebars that hide or render certain items based on the _page route_ the user navigated to. That is _powerful_!

For example, if we look at the image below inside the red circle, the _Editors_ part is an item of the sidebar, and the 3 items following immediately below (_Code Editor_, _Markdown_, _Text Editor_) are the subsections:

![sidebar example subsections 3 items](https://jsmanifest.s3-us-west-1.amazonaws.com/posts/create-a-modern-dynamic-sidebar-in-react-using-recursion/subsection-example1.jpg)

You will see by the end of this post that this seemingly complicated sidebar is actually _under 50 lines of code!_ What?!

Here is a _basic_ example of how you can extend the sidebar component from this post to be a little more stylish while still retaining the clean feel of it:

![modern dynamic sidebar in react using recursion extended](https://jsmanifest.s3-us-west-1.amazonaws.com/posts/create-a-modern-dynamic-sidebar-in-react-using-recursion/dazzling-sidebar-in-react-bonus.gif)

Without further ado, let's get started!

In this tutorial we are going to quickly generate a react project with [create-react-app](https://create-react-app.dev/docs/getting-started/).

(If you want to get a copy of the repository from github, click [here](https://github.com/jsmanifest/modern-sidebar)).

Go ahead and create a project using the command below. For this tutorial i’ll call our project _modern-sidebar_.

```bash
npx create-react-app modern-sidebar
```

Now go into the directory once it's done:

```bash
cd modern-sidebar
```

Inside the main entry `src/index.js` we're going to clean it up a bit so we can focus on the component alone:

```jsx
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './styles.css'
import * as serviceWorker from './serviceWorker'

ReactDOM.render(<App />, document.getElementById('root'))

serviceWorker.unregister()
```

Now create `src/App.js`:

```jsx
import React from 'react'

const App = () => <div />

export default App
```

`App` will be importing and using our `Sidebar` component by creating `Sidebar.js`, so lets go ahead and create that:

```jsx
import React from 'react'

function Sidebar() {
  return null
}

export default Sidebar
```

Now i'm going to install a CSS library, but you can actually achieve the same working functionality of the sidebar that we will be building without it. The reason I'm doing this is because I like seeing the additional ripple effects in addition to having icons readily available to use :)

```bash
npm install @material-ui/core @material-ui/icons
```

Once that is installed, we need to think of a base structure in the user interface that our sidebar will be built upon. A solution is to use the unordered list (`<ul>`) element that renders list items (`<li>`). We will import `List` and `ListItem` from `@material-ui/core` since the `List` component is essentially a `ul` element, and the `ListItem` component is essentially a `li`.

Lets start off hardcoding a couple of items in the sidebar to visualize how this might look like to boost our confidence. Sometimes a little extra confidence can help improve our productivity:

```jsx
import React from 'react'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemText from '@material-ui/core/ListItemText'

function Sidebar() {
  return (
    <List disablePadding dense>
      <ListItem button>
        <ListItemText>Home</ListItemText>
      </ListItem>
      <ListItem button>
        <ListItemText>Billing</ListItemText>
      </ListItem>
      <ListItem button>
        <ListItemText>Settings</ListItemText>
      </ListItem>
    </List>
  )
}

export default Sidebar
```

(`disablePadding` and `dense` were used to slightly shrink the size of each of the items, and the `button` prop was used to add the stunning ripple effect).

This is what we have so far:

![sidebar items list items hardcoded in react](https://jsmanifest.s3-us-west-1.amazonaws.com/posts/create-a-modern-dynamic-sidebar-in-react-using-recursion/sidebar-react-1.gif)

Now that we have boosted our confidence, let's go ahead and define `props.items`, which `Sidebar` will consume to render its items.

With that said, we're also going to expect an `items` prop that is an array of objects representing each item in the sidebar menu. We want to keep the functionality as simple as possible or else we could quickly overcomplicate the component.

Lets first create items in the `App` component and pass it as `props.items` to `Sidebar`:

```jsx
import React from 'react'
import Sidebar from './Sidebar'

const items = [
  { name: 'home', label: 'Home' },
  { name: 'billing', label: 'Billing' },
  { name: 'settings', label: 'Settings' },
]

function App() {
  return (
    <div>
      <Sidebar items={items} />
    </div>
  )
}

export default App
```

We will now update the `Sidebar` component to reflect this array structure:

```jsx
import React from 'react'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemText from '@material-ui/core/ListItemText'

function Sidebar({ items }) {
  return (
    <List disablePadding dense>
      {items.map(({ label, name, ...rest }) => (
        <ListItem key={name} button {...rest}>
          <ListItemText>{label}</ListItemText>
        </ListItem>
      ))}
    </List>
  )
}

export default Sidebar
```

One thing you might have noticed is that our sidebar is just _too dang big!_ *Side*bars usually take up one side of the screen. So what we are going to do is shrink its width to a suitable size. We will go ahead and put a `max-width` of `200px` on it. So we're going to create a `div` element that wraps our `List` component.

The reason why we create another `div` element instead of directly applying the styles on the `List` component is because we don't want to make `List` responsible for the width size. This way, in the future we can choose to abstract the `List` into a reusable sidebar component where it is able to adapt to any size depending on the size of the _parent_ element:

Here is the `Sidebar.js` component:

```jsx
import React from 'react'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemText from '@material-ui/core/ListItemText'

function Sidebar({ items }) {
  return (
    <div className="sidebar">
      <List disablePadding dense>
        {items.map(({ label, name, ...rest }) => (
          <ListItem key={name} button {...rest}>
            <ListItemText>{label}</ListItemText>
          </ListItem>
        ))}
      </List>
    </div>
  )
}

export default Sidebar
```

And inside `index.css` we defined the css styles for the `sidebar` class:

```css
.sidebar {
  max-width: 240px;
  border: 1px solid rgba(0, 0, 0, 0.1);
}
```

Material-UI actually uses their own [CSS styling mechanism](https://material-ui.com/styles/basics/) using the CSS-in-JS approach. But we will stick to regular CSS in this article to keep things unnecessarily complicated.

We can already just leave it as basic as this and call it a day. However, it doesn't support sub items. We want to be able to click on a sidebar item and have it drop down its list of sub items if it has any. Having sub items helps organize the sidebar by grouping additional items within another sidebar _section_:

![sidebar items with sub items in react](https://jsmanifest.s3-us-west-1.amazonaws.com/posts/create-a-modern-dynamic-sidebar-in-react-using-recursion/sidebar-react-1-with-subitems.gif)

The way we are going to support this feature is to allow another option inside _each_ sidebar item that the component will use to detect for its sub items. (Can you feel the _recursion_ coming?)

Let's change up our items array in the `App` component to pass in sub items:

```jsx
import React from 'react'
import Sidebar from './Sidebar'

const items = [
  { name: 'home', label: 'Home' },
  {
    name: 'billing',
    label: 'Billing',
    items: [
      { name: 'statements', label: 'Statements' },
      { name: 'reports', label: 'Reports' },
    ],
  },
  {
    name: 'settings',
    label: 'Settings',
    items: [{ name: 'profile', label: 'Profile' }],
  },
]

function App() {
  return (
    <div>
      <Sidebar items={items} />
    </div>
  )
}

export default App
```

To be able to render a sidebar item's _subitems_, we'd have to watch for the `items` property when rendering sidebar items:

```jsx
function Sidebar({ items }) {
  return (
    <div className="sidebar">
      <List disablePadding dense>
        {items.map(({ label, name, items: subItems, ...rest }) => (
          <ListItem style={{ paddingLeft: 18 }} key={name} button {...rest}>
            <ListItemText>{label}</ListItemText>
            {Array.isArray(subItems) ? (
              <List disablePadding>
                {subItems.map((subItem) => (
                  <ListItem key={subItem.name} button>
                    <ListItemText className="sidebar-item-text">
                      {subItem.label}
                    </ListItemText>
                  </ListItem>
                ))}
              </List>
            ) : null}
          </ListItem>
        ))}
      </List>
    </div>
  )
}
```

And now... behold, our _dazzling_ sidebar component!

![dazzling react sidebar component maybe not](https://jsmanifest.s3-us-west-1.amazonaws.com/posts/create-a-modern-dynamic-sidebar-in-react-using-recursion/dazzling-sidebar-in-react-maybe-not.gif)

If you haven't caught on already, this is _not_ the sidebar look that we want to _achieve_.

Now since we don't want our users to hit their close button on their browser and never come back to our web site, we need to figure out a way to make this look more appealing not only to the eyes, but to the _DOM_ as well.

"What do you mean the _DOM_", you ask?

Well, if you look closely, there's a problem! If the user clicks on a sub item, the parent item rendering the sub item is also consuming the click handler, since they are overlapping! This is _bad_ and calls upon some nasty unexpecting issues for the user's experience.

What we need to do is _separate the parent from its children (the sub items)_ so that they render their sub items _adjacently_, so that mouse events do not clash:

```jsx
function Sidebar({ items }) {
  return (
    <div className="sidebar">
      <List disablePadding dense>
        {items.map(({ label, name, items: subItems, ...rest }) => (
          <React.Fragment key={name}>
            <ListItem style={{ paddingLeft: 18 }} button {...rest}>
              <ListItemText>{label}</ListItemText>
            </ListItem>
            {Array.isArray(subItems) ? (
              <List disablePadding>
                {subItems.map((subItem) => (
                  <ListItem key={subItem.name} button>
                    <ListItemText className="sidebar-item-text">
                      {subItem.label}
                    </ListItemText>
                  </ListItem>
                ))}
              </List>
            ) : null}
          </React.Fragment>
        ))}
      </List>
    </div>
  )
}
```

Now we're _almost_ back in business!

![dazzing sidebar in react maybe so](https://jsmanifest.s3-us-west-1.amazonaws.com/posts/create-a-modern-dynamic-sidebar-in-react-using-recursion/dazzling-sidebar-in-react-maybe-getting-there.gif)

From the screenshot, it seems as though we have a new problem: the sub items are awkwardly larger than the top level items. We must figure out a way to detect which ones are sub items and which ones are top level ones.

We can hardcode this and call it a day:

```jsx
function Sidebar({ items }) {
  return (
    <div className="sidebar">
      <List disablePadding dense>
        {items.map(({ label, name, items: subItems, ...rest }) => {
          return (
            <React.Fragment key={name}>
              <ListItem style={{ paddingLeft: 18 }} button {...rest}>
                <ListItemText>{label}</ListItemText>
              </ListItem>
              {Array.isArray(subItems) ? (
                <List disablePadding dense>
                  {subItems.map((subItem) => {
                    return (
                      <ListItem
                        key={subItem.name}
                        style={{ paddingLeft: 36 }}
                        button
                        dense
                      >
                        <ListItemText>
                          <span className="sidebar-subitem-text">
                            {subItem.label}
                          </span>
                        </ListItemText>
                      </ListItem>
                    )
                  })}
                </List>
              ) : null}
            </React.Fragment>
          )
        })}
      </List>
    </div>
  )
}
```

```css
.sidebar-subitem-text {
  font-size: 0.8rem;
}
```

But our sidebar component is supposed to be dynamic. Ideally we want it to generate its items accordingly to the items passed in as props from the caller.

We're going to use a simple `depth` prop that the sidebar items will use, and based on the depth they can adjust their own spacing accordingly to `depth` no matter how far down the tree they're in. We're also going to extract out the sidebar item into its own component so that we can increase the depth without having to complicate it with introducing state logic.

Here is the code:

```jsx
function SidebarItem({ label, items, depthStep = 10, depth = 0, ...rest }) {
  return (
    <>
      <ListItem button dense {...rest}>
        <ListItemText style={{ paddingLeft: depth * depthStep }}>
          <span>{label}</span>
        </ListItemText>
      </ListItem>
      {Array.isArray(items) ? (
        <List disablePadding dense>
          {items.map((subItem) => (
            <SidebarItem
              key={subItem.name}
              depth={depth + 1}
              depthStep={depthStep}
              {...subItem}
            />
          ))}
        </List>
      ) : null}
    </>
  )
}

function Sidebar({ items, depthStep, depth }) {
  return (
    <div className="sidebar">
      <List disablePadding dense>
        {items.map((sidebarItem, index) => (
          <SidebarItem
            key={`${sidebarItem.name}${index}`}
            depthStep={depthStep}
            depth={depth}
            {...sidebarItem}
          />
        ))}
      </List>
    </div>
  )
}
```

So what's going on here?

Well, we declared some powerful props to configure the sidebar pre-render phase such as `depth` and `depthStep`. `SidebarItem` was extracted out into its own component and inside its render block it uses `depth` to calculate its spacing. The higher the `depth` is, the more deep down in the tree they're located in.

That's all possible because of this line:

```jsx
{
  items.map((subItem) => (
    <SidebarItem
      key={subItem.name}
      depth={depth + 1}
      depthStep={depthStep}
      {...subItem}
    />
  ))
}
```

`depth` gets incremented by `1` every time a new list of sub items goes deeper.

And the _recursion_ exists inside `SidebarItem` because it calls itself until there is no longer a _base case_, in other words when the array is empty then this piece of code automatically stops:

```jsx
{
  items.map((subItem) => (
    <SidebarItem
      key={subItem.name}
      depth={depth + 1}
      depthStep={depthStep}
      {...subItem}
    />
  ))
}
```

Lets test the recursionized sidebar component out now:

> src/App.js

```jsx
const items = [
  { name: 'home', label: 'Home' },
  {
    name: 'billing',
    label: 'Billing',
    items: [
      { name: 'statements', label: 'Statements' },
      { name: 'reports', label: 'Reports' },
    ],
  },
  {
    name: 'settings',
    label: 'Settings',
    items: [
      { name: 'profile', label: 'Profile' },
      { name: 'insurance', label: 'Insurance' },
      {
        name: 'notifications',
        label: 'Notifications',
        items: [
          { name: 'email', label: 'Email' },
          {
            name: 'desktop',
            label: 'Desktop',
            items: [
              { name: 'schedule', label: 'Schedule' },
              { name: 'frequency', label: 'Frequency' },
            ],
          },
          { name: 'sms', label: 'SMS' },
        ],
      },
    ],
  },
]

function App() {
  return (
    <div>
      <Sidebar items={items} />
    </div>
  )
}
```

![dazzling sidebar in react recursionized](https://jsmanifest.s3-us-west-1.amazonaws.com/posts/create-a-modern-dynamic-sidebar-in-react-using-recursion/dazzling-sidebar-in-react-recursionized.gif)

And there we have it!

Let's play with `depthStep` a little and pass in a higher value:

```jsx
function App() {
  return (
    <div>
      <Sidebar items={items} />
    </div>
  )
}
```

![dazzling sidebar in react recursionized #2](https://jsmanifest.s3-us-west-1.amazonaws.com/posts/create-a-modern-dynamic-sidebar-in-react-using-recursion/dazzling-sidebar-in-react-recursionized-2.gif)

## Conclusion

You can optionally download the repo from the [github link](https://github.com/jsmanifest/modern-sidebar) and see additional features of the sidebar. It features more fancy functionality such as adding an additional layer in rendering (sidebar _sections_) which leads to (_dividers_) as separators, sidebar expansion/collapsing, icons, etc.

I hope you found this to be valuable and look out for more in the future!

Find me on [medium](https://medium.com/@jsmanifest)