A navbar is one of the most important parts of a website. Users don't know where to go without one. Lets dive into how to make a responsive navbar in Next.js with tailwindcss. I'll also show you how to make the navbar appear across all pages.
If you don't want to read the whole thing and just want the code go to https://github.com/dmuraco3/navbar
Setting up our environment
creating our project
npx create-next-app@latest navbar
cd navbar
installing Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
configuring template paths
tailwind.config.js
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Add the Tailwind directives to your CSS
globals.css
...
@tailwind base;
@tailwind components;
@tailwind utilities;
...
now our environment is all set up and we're ready to go
Building our navbar
create a components folder in your root directory to keep components in.
in this folder make a new file called navbar.js
We're going to create three functions and import one
navbar.js
import {useState} from 'react'
function NavLink({to, children}) {
}
function MobileNav({open, setOpen}) {
}
export default function Navbar() {
}
Let's build the mobile nav first
navbar.js
...
function MobileNav({open, setOpen}) {
return (
<div className={`absolute top-0 left-0 h-screen w-screen bg-white transform ${open ? "-translate-x-0" : "-translate-x-full"} transition-transform duration-300 ease-in-out filter drop-shadow-md `}>
<div className="flex items-center justify-center filter drop-shadow-md bg-white h-20"> {/*logo container*/}
<a className="text-xl font-semibold" href="/">LOGO</a>
</div>
<div className="flex flex-col ml-4">
<a className="text-xl font-medium my-4" href="/about" onClick={() => setTimeout(() => {setOpen(!open)}, 100)}>
About
</a>
<a className="text-xl font-normal my-4" href="/contact" onClick={() => setTimeout(() => {setOpen(!open)}, 100)}>
Contact
</a>
</div>
</div>
)
}
...
now we can build our navbar
navbar.js
export default function Navbar() {
const [open, setOpen] = useState(false)
return (
<nav className="flex filter drop-shadow-md bg-white px-4 py-4 h-20 items-center">
<MobileNav open={open} setOpen={setOpen}/>
<div className="w-3/12 flex items-center">
<a className="text-2xl font-semibold" href="/">LOGO</a>
</div>
<div className="w-9/12 flex justify-end items-center">
<div className="z-50 flex relative w-8 h-8 flex-col justify-between items-center md:hidden" onClick={() => {
setOpen(!open)
}}>
{/* hamburger button */}
<span className={`h-1 w-full bg-black rounded-lg transform transition duration-300 ease-in-out ${open ? "rotate-45 translate-y-3.5" : ""}`} />
<span className={`h-1 w-full bg-black rounded-lg transition-all duration-300 ease-in-out ${open ? "w-0" : "w-full"}`} />
<span className={`h-1 w-full bg-black rounded-lg transform transition duration-300 ease-in-out ${open ? "-rotate-45 -translate-y-3.5" : ""}`} />
</div>
<div className="hidden md:flex">
<NavLink to="/contact">
CONTACT
</NavLink>
<NavLink to="/about">
ABOUT
</NavLink>
</div>
</div>
</nav>
)
}
Now our navbar depends on NavLink
which is a pretty simple component so lets whip that up real quick
navbar.js
function NavLink({to, children}) {
return <a href={to} className={`mx-4`}>
{children}
</a>
}
Now put that all together and we get
navbar.js
import { useState } from 'react'
function NavLink({to, children}) {
return <a href={to} className={`mx-4`}>
{children}
</a>
}
function MobileNav({open, setOpen}) {
return (
<div className={`absolute top-0 left-0 h-screen w-screen bg-white transform ${open ? "-translate-x-0" : "-translate-x-full"} transition-transform duration-300 ease-in-out filter drop-shadow-md `}>
<div className="flex items-center justify-center filter drop-shadow-md bg-white h-20"> {/*logo container*/}
<a className="text-xl font-semibold" href="/">LOGO</a>
</div>
<div className="flex flex-col ml-4">
<a className="text-xl font-medium my-4" href="/about" onClick={() => setTimeout(() => {setOpen(!open)}, 100)}>
About
</a>
<a className="text-xl font-normal my-4" href="/contact" onClick={() => setTimeout(() => {setOpen(!open)}, 100)}>
Contact
</a>
</div>
</div>
)
}
export default function Navbar() {
const [open, setOpen] = useState(false)
return (
<nav className="flex filter drop-shadow-md bg-white px-4 py-4 h-20 items-center">
<MobileNav open={open} setOpen={setOpen}/>
<div className="w-3/12 flex items-center">
<a className="text-2xl font-semibold" href="/">LOGO</a>
</div>
<div className="w-9/12 flex justify-end items-center">
<div className="z-50 flex relative w-8 h-8 flex-col justify-between items-center md:hidden" onClick={() => {
setOpen(!open)
}}>
{/* hamburger button */}
<span className={`h-1 w-full bg-black rounded-lg transform transition duration-300 ease-in-out ${open ? "rotate-45 translate-y-3.5" : ""}`} />
<span className={`h-1 w-full bg-black rounded-lg transition-all duration-300 ease-in-out ${open ? "w-0" : "w-full"}`} />
<span className={`h-1 w-full bg-black rounded-lg transform transition duration-300 ease-in-out ${open ? "-rotate-45 -translate-y-3.5" : ""}`} />
</div>
<div className="hidden md:flex">
<NavLink to="/contact">
CONTACT
</NavLink>
<NavLink to="/about">
ABOUT
</NavLink>
</div>
</div>
</nav>
)
}
Making navbar appear on all pages
Now we have our navbar made but we can't see it. We could go into each page function and add our components but that gets repetitive and there's a better way to do it.
In our root level file _app.js
we have to import the Navbar component and render it in the root level component
_app.js
import Navbar from '../components/navbar'
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <>
<Navbar/>
<Component {...pageProps} />
</>
}
export default MyApp
Now you have a working responsive navbar.
Thanks for reading!
Top comments (4)
Thanks for the post. But could you explain, why I should create 2 different navigations, if tailwind (or even vanilla css) could handle it with one nav, too.
I personally think, that more redundance delivers more errors.
Thanks for commenting. I Pulled this out of a recent project I've been working on and I chose to separate the code like this to make it more readable and more easy to maintain.
the a tag doesn't work with the '/' route ...can you help?
You have to surround the links with
and remove the href="/" from the a tag. Also be sure to import Link from "next/link";