How to implement responsive mobile nav menu with Next.Js?

Viewed 57

So, I'm a bit confused on how to implement a responsive mobile navigation in Next.js. My problem stems from handling the menu toggle state both via Javascript and CSS.

My nav menu follows the following tutorial from Log Rocket, but the methods employed in the tutorial were written for React and do not account for how Next.js applies CSS class names. The plain React example depends on dynamicaly updating the CSS, while it seems that most Next.js approaches suggest toggling the element with useState. This works, but I lose the granularity of being able to control the navigation through media queries and the toggle button.

My current implementation checks a boolean useState value to show the menu, which I want to only apply to the menu at 780px or lower. If I start with a state of false, the whole menu isn't rendered including the button to toggle it. If I start with a state of true, then the mobile menu is rendered as expanded automatically, which I want to prevent. This also resuls in the toggle removing the menu altogether when the user isn't on mobile (e.g. if the menu is untoggled, and the user goes to a large screen size.

So, the question becomes how to prevent this unintended behavior? Or how to set the mobile menu default state to be hidden while preserving the click to toggle functionality?

import { useState } from 'react'
import Link from 'next/Link'
import SiteLogo from '../components/CompLogo'
import navStyles from '../styles/Nav.module.css'


const Nav = () =>{

const [isNavExpanded, setIsNavExpanded] = useState(true)
return(
<div>
<nav className={navStyles.nav}>
<SiteLogo />
<button className={navStyles.hamburger} onClick={() => {
          setIsNavExpanded(!isNavExpanded);
          console.log("clicked");
          console.log(isNavExpanded);
        }}>
{/* icon from heroicons.com */}
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
  <path fillRule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clipRule="evenodd" />
</svg>

</button>
<div className={navStyles.navigation}>
    {isNavExpanded && 
    <ul className={navStyles.menu}>
        <li className={navStyles.links}>
            <Link href='/'>
                <a className={navStyles.anchors}>Home</a>
            </Link>
        </li>
        <li className={navStyles.links}>
            <Link href='/map'>
            <a className={navStyles.anchors}>Map Page</a>
                </Link>
        </li>
        <li className={navStyles.links}>
            <Link href='/about' >
            <a className={navStyles.anchors}>About</a>
                </Link>
        </li>
    </ul>
}
</div>
</nav>
</div>
    )
}

export default Nav 

Here's my css:

.nav {
background-color: white;
color: black;
width: 100%;
display: flex;
align-items: center;
position: relative;
padding: 0.5rem 0 rem;
}

.navigation {
    margin-left:auto;
}


.menu{
display: flex;
padding: 0;
}

.links{
list-style-type: none;
margin: 0 1rem;    
}

.links a {
text-decoration: none;
display: block;
width: 100%;
}

.hamburger{
border: 0;
height: 80px;
width: 80px;
padding: 0.5rem;
border-radius: 50%;
background-color: white;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
position: absolute;
top: 50%;
right: 5px;
transform: translateY(-50%);
display: none;
}




@media screen and (max-width: 768px) {
    .hamburger {
        display: block;
      }
      .menu {
        position: absolute;
        top: 146px;
        left: 0;
        flex-direction: column;
        width: 100%;
        height: calc(100vh - 147px);
        background-color: white;
        border-top: 1px solid black;
    
      }

      .links {
        text-align: center;
        margin: 0;
      }
      .links a {
        color: black;
        width: 100%;
        padding: 1.5rem 0;
      }
      .links:hover {
        background-color: #eee;
      }
      .menu.expanded {
        display: block;
      }
}
0 Answers
Related