All Articles

How to create Active Nav Link in NextJS

 —  #Next.js #React #StyledComponents #DevTips

How does one target active Link in Next.js like the way we do it in React-Router? While I was building my blog I wanted to provide a user an indication where they are on my site, hoping I could just do it the same way as in a standard React App.

The Next.js default Link component offered in next/link doesn't allow to distinguish between active and non-active links. However, you can use the built-in router to detect an active link.

Let's have a look at a simple example.

import Link from "next/link"; import { useRouter } from "next/router"; /* You can have a config file somewhere else in your folder structure or use it as below */ const navLinks = [ { title: 'Home', path: '/' }, { title: 'Projects', path: '/projects' }, { title: 'Blog', path: '/blog' }, { title: 'About', path: '/about' }, export const MyNav = () => { const router = useRouter(); return ( <ul> {navLinks.map((link) => ( <li key={link.title}> <Link href={link.path} passHref> <a className={router.pathname === link.path ? "activeLink" : " "}> {link.title} </a> </Link> </li> ))} </ul> ); };

The solution above is simple and for a simple site. There are a few “gotchas” in regards to Next.js Link especially when you are using components libraries (StyledComponents ,...etc) .next/link docs demonstrate some caveats when native HTML <a> tag is styled using some CSS-in-JSS libraries.

Let's tackle another technique.

We create our own Link component, and we store it in a file ActiveLink.js in the /components folder, and import that instead of the default next/link.

import { useRouter } from "next/router"; import PropTypes from "prop-types"; import Link from "next/link"; import React, { Children } from "react"; const ActiveLink = ({ children, activeClassName, ...props }) => { const { asPath } = useRouter(); const child = Children.only(children); const childClassName = child.props.className || ""; // pages/index.js will be matched via props.href // pages/about.js will be matched via props.href // pages/[slug].js will be matched via props.as const className = asPath === props.href || asPath === props.as ? `${childClassName} ${activeClassName}`.trim() : childClassName; return ( <Link {...props}> {React.cloneElement(child, { className: className || null, })} </Link> ); }; ActiveLink.propTypes = { activeClassName: PropTypes.string.isRequired, }; export default ActiveLink;

Then we have our Nav component. Styling of ActiveLink is based on CSS of your choice be it component libraries, Tailwind…

import ActiveLink from "./components/ActiveLink"; const Nav = () => ( <nav> <ul className="nav"> <li> <ActiveLink activeClassName="active" href="/"> <a className="nav-link">Home</a> </ActiveLink> </li> <li> <ActiveLink activeClassName="active" href="/about"> <a className="nav-link">About</a> </ActiveLink> </li> <li> <ActiveLink activeClassName="active" href="/[slug]" as="/dynamic-route"> <a className="nav-link">Dynamic Route</a> </ActiveLink> </li> </ul> </nav> ); export default Nav;

Finally, we can implement our Navigation bar on our page.

import Nav from "../components/Nav"; export default () => ( <div> <Nav /> <p>Hello, I'm the home page</p> </div> );

Just a quick note; the above implementation only goes one level deep, meaning, if you would like to have your NavLink active for nested routes you should check out some neat solutions on stackoverflow here.

If you find this little piece helpful or if there are any tips for improvement, find me on Twitter or just a drop a line.