Skip to content

Instantly share code, notes, and snippets.

@alexgeis
Last active January 23, 2023 18:13
Show Gist options
  • Select an option

  • Save alexgeis/4ff1b2d7b26d074879bd1513a9fbef97 to your computer and use it in GitHub Desktop.

Select an option

Save alexgeis/4ff1b2d7b26d074879bd1513a9fbef97 to your computer and use it in GitHub Desktop.
NavBar + Hamburger Menu w/ Active Link Styling in Next.js 13

NavBar + Hamburger Menu w/ Active Link Styling in Next.js 13

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.

Table of Contents
  1. JSX Code
  2. CSS Code

JSX Code

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>
	);
}

(back to top)

CSS Code

Notes:

  1. Provided colors are basic and should be replaced
  2. 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;
	}
}

(back to top)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment