DEV Community

Cover image for Build a Simple Responsive Menu with React and CSS
Jason Melton
Jason Melton

Posted on • Edited on

Build a Simple Responsive Menu with React and CSS

I built a little portfolio site for some of my projects. Filled with pride, I shared the link for feedback and got an instant reply of “What is this trash?”

Alt Text

The text is pushed off the screen. The navigation links are just gone — four components impossible to find.

My portfolio was a one button page where all you can do is refresh. Refresh if you want, it never gets fresher.

To solve my responsiveness issues, I studied an article on CSS-Tricks about responsive menus and incorporated some of the ideas there to create a responsive menu with React and CSS.

Jump to GitHub with demo gif in Readme

The Key

@media screen and (max-width: 44em) {
  .nav-small{
    display: block
  }
  .navbar{
    display: none
  }
}

Don’t bother skimming the rest of the blog. This is it: CSS @media Rule. Basically, this rule says “Once, a screen hits this condition, change all the following CSS…”

Using React, I simply created two containers for navigation options. The first, with class “navbar”, displays my navigation options in a typical header menu kinda way. The other, with class “nav-small”, is drop down menu.

In the code above, if the screen size is greater than 44em, the full menu displays. Any smaller than that, you get the drop down menu.

Note: I’ve since learned that many front-end developers ascribe to a “Mobile First” mindset with CSS. This generally means a preference for min-width over max-width media queries. Once it looks good on small screens, it’s more natural to scale up to larger screens. There are many benefits to this approach, but I will leave my code as is for the time being. Something to consider!

My Plan

  1. Create a menu container with separate component for navigation options.
  2. Create a second menu container that is a drop down for the same navigation options.
  3. Change the CSS display property using the @media rule. The Details

Full Screen Menu Container and Options

Alt Text

First, I built the full screen menu. Nothing special here. It is a simple functional component that displays the options. Remember, since it has a class of “navbar”, it will not display on small screens.

import React from 'react';
import '../App.css';
import NavOptions from './NavOptions'

const Navbar = () => {

    return (
        <nav className="navbar">
            <NavOptions/>
        </nav>
    );
}
export default Navbar;

The navigation options get a little bit complex.

import React, {useState} from 'react';
import { withRouter } from 'react-router-dom';

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

const NavOptions = (props) => {
    const {handleToggle, history} = props
    const [activeItem, setActiveItem] = useState('home')

    //navigation menu options
    const navOptions = [
        "home", 
        "t-rex", 
        "stegasaurus", 
        "triceratops"
    ]

    const handleClick = (e) => {
        //close menu drop down
        handleToggle && handleToggle()

        //push to route
        let clickedNav = e.target.getAttribute("nav")
        setActiveItem(clickedNav)
        clickedNav === 'home'
            ? history.push('/')
                : history.push(`${clickedNav}`)
    }

    return (
        navOptions.map(option => <div 
                className={"nav-option" + (activeItem === option ? " clicked" : '')}
                onClick={handleClick} 
                nav={option}
                key={option} 
            >
                {option}
            </div>
        )
    )
}

export default withRouter(NavOptions);

Sorry, this is a lot! To summarize the above code:

  1. When the component is created, it sets the state to have an “activeItem” of “home.” This is how I select the “home” option to be highlighted with a different color (using CSS) when the component first renders.
  2. The component renders the navigation options by mapping an array of strings into individual divs. The strings are names for the locations you’d like to have on your menu.
  3. The navigation option divs contain a ternary class name that allows me to apply special CSS to the one that is selected based on the component’s state.
  4. The divs also contain an onClick event handler which, using React Router, pushes the user to the same path as the class. Also, if the click happens from the small screen menu, it gets passed the function handleToggle() as props which closes the drop down menu. More on that in the next section.

Small Screen Menu

Alt Text
Next, I built the drop down menu. This is a little more complex than the other container because it has toggle functionality.

import React, {useState} from 'react';

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

//components
import NavOptions from './NavOptions';

const NavSmall = () => {
    const [toggleMenu, setToggleMenu] = useState(false)

    const handleToggle = () => {
        setToggleMenu(!toggleMenu)
    }

    return (
        <nav className="nav-small">
            <button 
                className="nav-small-button" 
                onClick={handleToggle}
            >
                Menu
            </button>
            {toggleMenu 
                ? <NavOptions handleToggle={handleToggle}/>
                    : ""}
        </nav>
    )
}
export default NavSmall;

This second menu has the class “nav-small” which will display on small screens but not on large screens due to the CSS.
CSS Display Properties and @media Rule
I already mentioned this final step above, but will throw the image back down here for posterity.

@media screen and (max-width: 44em) {
  .nav-small{
    display: block
  }
  .navbar{
    display: none
  }
}

Adding this rule sets which menu will be displayed based on screen size.

The rest of my CSS styles things to look nice for the demo — highlighting the drop down options on hover, etc. I assume you will adjust the CSS to your personal projects so I won’t go through the trouble of explaining all the code but you can check it out here.

If you would like another example, check out my portfolio site where I use the logic of this blog with Semantic UI React for nicer looking results.

Thanks for reading. Hope this helps someone!

Special thanks to Alexandria Pugia for cleaning up my code and Keith Burgia for tips on @media queries.

Top comments (3)

Collapse
 
guzzur profile image
Felix Razykov

Your site can’t provide a secure connection

Collapse
 
cooljasonmelton profile image
Jason Melton

Thanks for the heads up. I'm reading I can only get a secure connection by paying for a Heroku plan, and I'm trying to keep my budget tight for now. For the time being, do not input any sensitive info into my portfolio, lol.

Collapse
 
1000facesmusic profile image
1000Faces

Hello, I tried your code on my project but when i am reducing the size i can't see that nothing appears, did i have to import my navbar and the dropdown in my app.js ?