This gist is a guide for setting up a navigation bar (with working hamburger menu) and active link styling in Next.js 13.
(it assumes you have already installed version 13+ of Next.js)
Updating your navbar from a previous version of Next.js should be fairly straightforward - all queries that arise during this process should be directed to the (excellent) Next.js 13 beta docs.
I have used "TODO" markers to indicate the sections that require updates for your specific application.
Regarding styling, CSS modules are used but you can easily find all/replace instances of ".style" and update to other styling methods.
Navbar.jsx
"use client";
import styles from "./NavBar.module.css"; // CSS module import
import { useState } from "react";
import Image from "next/image";
import exampleImg from "./assets/example.png"; // TODO: replace image import
import Link from "next/link";
import { usePathname } from "next/navigation";
export function NavBar() {
const pathname = usePathname(); // this replaces Next <12 useRouter() and the property "asPath"
// logic for hamburger menu
const [hamOpen, setHamOpen] = useState(false);
const hamburgerMenuToggle = () => {
setHamOpen(!hamOpen);
};
const closeMenu = () => {
setHamOpen(false);
};
return (
<div className={styles.wrapper}>
<div className={styles.logoWrapper}>
<Link
href="/"
className={styles.logoLink}
>
<Image
src={exampleImg}
alt="example image logo"
className={styles.logo}
width={100} // TODO: UPDATE WIDTH AND HEIGHT AS NEEDED
height={100}
></Image>
</Link>
{/* PLACEHOLDER TEXT, IF NEEDED */}
</div>
<div className={styles.nav}>
<div className={pathname == "/linkOne" ? styles.navLinkActive : ""}> // TODO: update pathname
<Link
href="linkOne" // TODO: update pathname
className={styles.navLink}
>
LINK_ONE // TODO: replace link text
</Link>
</div>
</div>
{/* HAMBERDER */}
<div className={styles.hamburgerWrapper}>
<div
className={styles.hamburgerBox}
onClick={hamburgerMenuToggle}
>
<div className={`${hamOpen ? styles.hamburgerLineOne : ""}`}></div>
<div className={`${hamOpen ? styles.hamburgerLineTwo : ""}`}></div>
<div className={`${hamOpen ? styles.hamburgerLineThree : ""}`}></div>
</div>
<div
className={`${styles.hamburgerMenu} ${
hamOpen ? styles.hamburgerMenuActive : ""
}`}
>
{/* <div
className={
pathname == "/" // TODO: replace pathname
? styles.hamburgerLinkActive
: styles.hamburgerLink
}
>
<Link
href="/" // TODO: replace pathname
onClick={() => closeMenu()}
>
HOME // TODO: replace link text
</Link>
</div> */}
<div
className={
pathname == "/linkOne" // TODO: replace pathname
? styles.hamburgerLinkActive
: styles.hamburgerLink
}
>
<Link
href="/linkOne" // TODO: replace pathname
onClick={() => closeMenu()}
>
CONTACT // TODO: replace link text
</Link>
</div>
</div>
</div>
</div>
);
}
Notes:
- Provided colors are basic and should be replaced
- CSS is mobile-first, so media queries define behavior PAST the defined pixel amount (600px to start)
NavBar.module.css
.wrapper {
position: -webkit-sticky;
position: sticky;
z-index: 1001;
top: 0;
max-width: 1200px;
width: 100%;
padding: 10px;
margin: 0 auto;
display: flex;
justify-content: space-between;
background-color: black;
}
.logoWrapper {
display: flex;
align-items: center;
z-index: 1002;
}
.logoLink {
color: white;
font-weight: 700;
font-size: 32px;
}
.logoLink:hover {
cursor: pointer;
}
.logo {
// additional styles needed for image
// don't forget !important if you are overriding inline styles
}
.nav {
display: none;
box-sizing: border-box;
}
.navLink {
color: white;
}
.navLink:hover {
color: yellow;
}
.navLinkActive {
color: yellow;
padding-bottom: 5px;
font-size: 110%;
border-bottom: 3px solid white;
}
/* hamberder menu */
.hamburgerWrapper {
display: flex;
align-items: center;
margin: 0 20px;
}
.hamburgerBox {
display: inline-block;
cursor: pointer;
z-index: 1003;
}
.hamburgerBox div {
width: 35px;
height: 5px;
background-color: white;
margin: 6px 0;
transition: 0.4s;
}
.hamburgerMenu {
display: none;
}
.hamburgerMenuActive {
position: absolute;
display: grid;
/* place-items: center;
UNCOMMENT WHEN >1 menu items */
top: 0;
left: 0;
width: 100vw;
height: 100vh;
padding: 20vh 20px;
background-color: black;
text-align: center;
z-index: 1000 !important;
}
.hamburgerLink {
opacity: 1;
display: block;
font-size: 30px;
color: white;
}
.hamburgerLinkActive {
opacity: 1;
display: block;
font-size: 30px;
color: yellow;
}
.hamburgerLineOne {
transform: rotate(-45deg) translate(-9px, 6px);
}
.hamburgerLineTwo {
opacity: 0;
}
.hamburgerLineThree {
transform: rotate(45deg) translate(-8px, -8px);
}
@media only screen and (min-width: 600px) {
.nav {
display: flex;
align-items: center;
gap: 20px;
}
.hamburgerWrapper {
display: none;
}
}