Part 1: Build This Cool Dropdown Menu with React, React Router and CSS

cooljasonmelton

Jason Melton

Posted on September 21, 2020

Part 1: Build This Cool Dropdown Menu with React, React Router and CSS

How about a little dropdown menu to throw in a tight area of your site? Or maybe use it as a simple navigation tool with your minimalistic layout? Maybe use this smaller menu when the screen shrinks to smaller size.

dropdown demo

the code on GitHub

Tutorial

Table of Contents

*Preliminary Junk
*Menu Component
*Menu CSS
*React Router
*Conclusion

Preliminary Junk

To get it going, I made a create-react-app, installed React Router, deleted any unneeded default code, and set up a file structure that looks like this:

file structure

Menu Component

Menu.js contains all the JavaScript and JSX for our dropdown.

There are essentially four parts:

  1. A useState hook holding a boolean that dictates if the menu should open. I call this openMenu.

  2. A function called setClassNames() which conditionally adds classes to the menu items. The CSS of the classes will animate the menu.

  3. A function called pushToRoute() which employs React Router to render the correlated component to the clicked menu item.

  4. The Menu component’s return JSX for rendering the structure and bringing together all the functionality.

import React, {useState} from 'react';

// router
import { withRouter } from 'react-router-dom';

// styling
import './Menu.css';

const Menu = props => {
    // conditionally render dropdown affect based on this boolean
    const [openMenu, setOpenMenu] = useState(false)

    // parameter num corresponds to .open-# classes
    // is assigned when Menu clicked triggering animated dropdown
    const setClassNames = num => {
        const classArr = ["m-item"];
        if (openMenu) classArr.push(`open-${num}`)
        return classArr.join(' ')
    }

    // takes route string as parameter
    const pushToRoute = route => {
        props.history.push(route)
        setOpenMenu(false)
    }

    return (
        <div className="Menu">
            <div className={"m-item m-logo"}
                onClick={() => setOpenMenu(!openMenu)}>
                Menu
            </div>
            <div className={setClassNames(1)}
                onClick={() => pushToRoute("/dashboard")}>
                Dashboard
            </div>
            <div className={setClassNames(2)}
                onClick={() => pushToRoute("/settings")}>
                Settings
            </div>
            <div className={setClassNames(3)}
                onClick={() => pushToRoute("/")}>
                Sign out
            </div>
        </div>
  );
}

export default withRouter(Menu);

Enter fullscreen mode Exit fullscreen mode
Menu CSS

dropdown menu

CSS does all the work to open the menu. There are five important parts.

1 The .Menu class is the most outer container. This layer needs position: relative;.

The individual menu items will have a position: absolute;, so they will render based on the nearest component with a position. The basis of position will be the outer div of the Menu component.

2 The .m-item class is applied to each individual menu item. They are absolutely positioned with an initial top: 0;. This will render all the items on top of each other at the top of the .Menu div.

I use em units for width and all the other properties so I can ensure the items will fit perfectly on top of each other and still be responsive (em units are relative to the font-size of the element).

For aesthetics, I give them a background-color, box-shadow, padding, border-radius, font-size, and color.

The flexbox properties center the text vertically and horizontally. cursor changes the style of the mouse pointer on the screen to show the user menu items are clickable.

Finally, transition will animate changing properties applied by the setClassNames() function and CSS :hover.

.Menu{
    position: relative;
    margin: 2em 3em;
}

.m-item{
    position: absolute;
    top: 0;

    width: 5.5em;
    background-color: #301A4B;
    box-shadow: 1px 2px 2px #301A4B;

    padding: 0.2em;

    border-radius: 1em;

    display: flex;
    align-items: center;
    justify-content: center;

    font-size: 1.5em;
    color: #EDFFEC;
    cursor: pointer;

    transition: 1s;
}
Enter fullscreen mode Exit fullscreen mode

3 .m-item:hover adds a small border to the menu items on hover. The added 1 px of border causes the items to animate slightly giving them a little bit of life.

.m-item:hover{
    border: 1px solid black;
}
Enter fullscreen mode Exit fullscreen mode

4 .m-logo is a special class for the first menu item. The z-index: 1; brings this div to the top so all other divs can hide beneath it.

z-index has a default value of 0 so if only one item has it declared, 1 will be enough to bring it to the top of all else.

.m-logo{
    z-index: 1;
}
Enter fullscreen mode Exit fullscreen mode

5 A series of classes called .open-1, .open-2, and .open-3 cause the animation of the drop down. These classes are applied via setClassNames() when the the top menu item is clicked.

On click, each item transitions to the new properties in their open-# class. Namely, they will move to the newly specified top and new background-color.

.open-1{
    top: 1.8em;
    background-color: #9b5de5;
}
.open-2{
    top: 3.6em;
    background-color: #f15bb5;
}
.open-3{
    top: 5.4em;
    background-color: #00BBF9;
}
Enter fullscreen mode Exit fullscreen mode
React Router

All the aesthetic aspects of the Menu component are set up at this point. What's left is to set up React Router so clicking on items navigates you to the correct component.

I wired this up in three steps:

1 The App.js file is the main run file for the whole project so this is where I set up the basic Router stuff.

I wrap App's return with BrowserRouter so the soon-to-be Routes will be available to all components contained.

I set up a Switch so that when one Route is rendered, the others will be disabled. Within, I define each specific Route needed for the project (these are set to null for the sake of demonstration).

The Menu component is placed outside the Switch so it will render whenever App is rendering. This makes it an efficient navigation tool, available on any screen of the app.

import React from 'react';

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

// styling
import './App.css';

// components
import Menu from './Menu/Menu';

const App = () => {
  return (
    <BrowserRouter>
      <div className="App">
        {/* dropdown menu */}
        <Menu/>
        {/* routes */}
        <Switch>
          <Route exact path="/settings" component={null} />
          <Route exact path="/dashboard" component={null} />
          <Route exact path="/" component={null} />
        </Switch>
      </div>
    </BrowserRouter>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

2 In our Menu component, I import withRouter and wrap the Menu with it in the export statement.

// router
import { withRouter } from 'react-router-dom';
Enter fullscreen mode Exit fullscreen mode
export default withRouter(Menu);
Enter fullscreen mode Exit fullscreen mode

withRouter gives Menu props that will allow us to manipulate the react-router-dom. To access these, we give Menu a parameter of props.

const Menu = props => {
Enter fullscreen mode Exit fullscreen mode

3 Finally, I wrote the function pushToRoute() which takes a parameter string of a route and pushes our app to that route. Then, it closes the Menu by calling setOpenMen(false).

    // takes route string as parameter
    const pushToRoute = route => {
        props.history.push(route)
        setOpenMenu(false)
    }
Enter fullscreen mode Exit fullscreen mode

The menu items call pushToRoute() onClick.
For example:

   <div className={setClassNames(1)}
        onClick={() => pushToRoute("/dashboard")}>
        Dashboard
   </div>
Enter fullscreen mode Exit fullscreen mode
Conclusion

I enjoyed creating this menu. It’s an efficient and easy to code tool that could help in a number of scenarios. I hope you find the concepts useful.

I love feedback. Got any suggestions? I’d be happy to add your knowledge to my own. Hit me up below or email me: jason.melton2@gmail.com if you got a reason to.

Best! Jason Melton.

πŸ’– πŸ’ͺ πŸ™… 🚩
cooljasonmelton
Jason Melton

Posted on September 21, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related