Go back

Active nested routes in Next.js — How I figured it out.

I've been accustomed to using a straightforward approach when I want to create an active Nav item whenever I'm building Header components in Next.js.

What I do is rely on the use of the pathname property that next/router extends for me.

With that, I check if the current path is the same as what I'm map-ping through in my array of navItems.

const navLinks = [
  { name: 'Blog', path: '/blog' },
  { name: 'Projects', path: '/projects' },
  { name: 'Articles', path: '/articles' },
]

And I proceed to modify the element if the path in my array above matches the current pathname like so:

<ul>
  {navLinks.map((links, index) => {
    return (
      <Link href={links.path} key={index}>
        <li className={router.pathname === links.path ? 'active-link' : ''}>
          {links.name}
        </li>
      </Link>
    )
  })}
</ul>

While this seems entirely okay for now, at least. What happens when you have nested routes that are related to a parent route? Say the /blog route now, because it includes dynamic pages per each article on your website.

You'd need to provide a visual representation of the page(s) people are currently on. With the first approach, in the snippet above, this is omitted.


Since all nested routes are sort of treated as dynamic routes with slugs in the pages dir.

With this background knowledge, all I had to do was catch the dynamic routes in my ternary operation like so:

<ul>
  {navLinks.map((links, index) => {
    return (
      <Link href={links.path} key={index}>
        <li
          className={
            router.pathname === links.path ||
            router.pathname === `${links.path}/[slug]`
              ? 'active-link'
              : ''
          }
        >
          {links.name}
        </li>
      </Link>
    )
  })}
</ul>

If you happen to call your dynamic route another name aside slug then you'd be expected to change it accordingly in your component.


If you want to catch routes that are nested, probably, two layers deep in the filesystem, your conditional may appear similar to this one below.

{
  router.pathname === links.path ||
  router.pathname === `${links.path}/[slug]` ||
  router.pathname === `${links.path}/[slug]/[slug]`
    ? 'active-link'
    : ''
}

Let me know if this was in any way helpful to you.