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.
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:
Menu Component
Menu.js
contains all the JavaScript and JSX for our dropdown.
There are essentially four parts:
A
useState
hook holding a boolean that dictates if the menu should open. I call thisopenMenu
.A function called
setClassNames()
which conditionally adds classes to the menu items. The CSS of the classes will animate the menu.A function called
pushToRoute()
which employs React Router to render the correlated component to the clicked menu item.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);
Menu CSS
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;
}
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;
}
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;
}
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;
}
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;
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';
export default withRouter(Menu);
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 => {
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)
}
The menu items call pushToRoute()
onClick.
For example:
<div className={setClassNames(1)}
onClick={() => pushToRoute("/dashboard")}>
Dashboard
</div>
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.
Top comments (5)
This looks pretty cool, it reminds me a little of a FAB with speed dial.
One thing I'd say to think about is making the menu items customisable. Currently you have to edit your
Menu
class to make any changes.Generally when making a component the aim is to make it reusable and simple enough that is rarely needs to be revisited.
Here's something I wrote a little while ago that takes an object and builds the menu from that but there are many ways to do it.
If you do make any of changes it might be cool to do a part 2 of this post showing what you changed 😊.
Ah yeah! That's a good idea. Initially, I was going to do something like that and map all the menu items, but I thought this way it'd be more clear what I was doing for demonstration.
I do like your idea. I'll write a part 2 and then be able to compare and contrast. Thanks for that and thanks for sharing your stuff with me. Awesome.
How to set background image with clicking on the dropdown menu. I need help regarding this please
Some comments may only be visible to logged-in visitors. Sign in to view all comments.