DEV Community

Nikolas ⚡️
Nikolas ⚡️

Posted on • Originally published at nikolasbarwicki.com

Highlight Currently Active Link in Next.js 13 with App Router

Also available on my blog

In modern web applications, navigation is a critical aspect of providing an excellent user experience. One vital element of navigation is highlighting the active link, which gives users a clear indication of their current position within the application. Next.js 13 introduces the App router, which includes the usePathname hook for reading the current URL's pathname.

In this article, we will walk through setting up a sample Navigation component and create a helper function to check the active navigation link using the usePathname hook. We will also cover handling nested URLs and the root path.

Setting up a sample Navigation component using next/link

Before diving into creating the helper function to check the active navigation link, let's set up a simple Navigation component with a navigation structure. First, define the navigation array, which includes the href, an icon, and a name for each navigation item:

type NavigationItem = {
  href: string
  icon: React.ComponentType
  name: string
}

const navigation: NavigationItem[] = [
  { href: '/', icon: HomeIcon, name: 'Dashboard' },
  { href: '/feedback', icon: UsersIcon, name: 'Feedback' },
  { href: '/roadmap', icon: FolderIcon, name: 'Roadmap' },
  { href: '/comments', icon: CalendarIcon, name: 'Comments' },
]
Enter fullscreen mode Exit fullscreen mode

Now, let's create a simple Navigation component that renders the navigation items in a list:

import Link from 'next/link'

export function Navigation() {
  return (
    <nav>
      <ul>
        {navigation.map(({ href, icon: Icon, name }) => (
          <li key={href}>
            <Link href={href}>
              <Icon />
              <span>{name}</span>
            </Link>
          </li>
        ))}
      </ul>
    </nav>
  )
}
Enter fullscreen mode Exit fullscreen mode

Creating a basic isActivePath helper function with usePathname hook

The critical piece of our navigation is identifying the active link. We will use a helper function that will take the href of each navigation item and determine if it's active based on the current pathname.

Next.js 13 introduced the usePathname hook within the next/navigation package. Import the hook and use it to access the current pathname:

import { usePathname } from 'next/navigation'
Enter fullscreen mode Exit fullscreen mode

Now that we have the pathname, let's create a helper function that evaluates if the current URL matches the navigation item's href and eventually highlights the active navigation link.

import { usePathname } from 'next/navigation'

export function useActivePath(): (path: string) => boolean {
  const pathname = usePathname()

  const isActivePath = (path: string) => {
    return path === pathname
  }

  return isActivePath
}
Enter fullscreen mode Exit fullscreen mode

The isActivePath function compares the path argument to the current pathname. Next, update the Navigation component to use this helper and conditionally apply the "active" className for the current navigation link.

"use client";
import Link from 'next/link'
import { useActivePath } from './helper'

export function Navigation() {
  const isActivePath = useActivePath()

  return (
    <nav>
      <ul>
        {navigation.map(({ href, icon: Icon, name }) => (
          <li key={href}>
            <Link href={href} className={isActivePath(href) ? 'active' : ''}>
              <Icon />
              <span>{name}</span>
            </Link>
          </li>
        ))}
      </ul>
    </nav>
  )
}
Enter fullscreen mode Exit fullscreen mode

Handling nested paths and the root path issue

The initial implementation of our isActivePath function works well for simple URLs. However, when we navigate to nested paths like /feedback/id, the Feedback navigation link isn't active. To handle such cases, update the isActivePath function to match paths that start with the same URL segments:

import { usePathname } from 'next/navigation'

export function useActivePath(): (path: string) => boolean {
  const pathname = usePathname()

  const isActivePath = (path: string) => {
    return pathname.startsWith(path)
  }

  return isActivePath
}
Enter fullscreen mode Exit fullscreen mode

Now, the isActivePath function will return true for /feedback when the URL is /feedback/id.

However, with this implementation, when the URL is /feedback/id, both the root (/) and the Feedback links receive the active class. We want to ensure only the Feedback link is active in this case. To fix this, let's add a special case for the root path (/) in the isActivePath function:

import { usePathname } from 'next/navigation'

export function useActivePath(): (path: string) => boolean {
  const pathname = usePathname()

  const isActivePath = (path: string) => {
    if (path === '/' && pathname !== path) {
      return false
    }
    return pathname.startsWith(path)
  }

  return isActivePath
}
Enter fullscreen mode Exit fullscreen mode

Now, the root navigation item will only receive the active class when the URL is exactly equal to the root (/), ensuring the correct behavior of our Navigation component.

Final solution

Based on the changes discussed above, our final solution to check the active navigation link in Next.js 13 using the App router and the usePathname hook is:

// app/helper.ts
import { usePathname } from 'next/navigation'

export function useActivePath(): (path: string) => boolean {
  const pathname = usePathname()

  const isActivePath = (path: string) => {
    if (path === '/' && pathname !== path) {
      return false
    }
    return pathname.startsWith(path)
  }

  return isActivePath
}
Enter fullscreen mode Exit fullscreen mode

To use this hook inside a component, update the Navigation component as follows:

'use client'
import Link from 'next/link'
import { useActivePath } from './helper'

export function Navigation() {
  const isActivePath = useActivePath()

  return (
    <nav>
      <ul>
        {navigation.map(({ href, icon: Icon, name }) => (
          <li key={href}>
            <Link href={href} className={isActivePath(href) ? 'active' : ''}>
              <Icon />
              <span>{name}</span>
            </Link>
          </li>
        ))}
      </ul>
    </nav>
  )
}
Enter fullscreen mode Exit fullscreen mode

This helper function is now capable of handling the active navigation link for both simple and nested URLs while addressing the root path issue. By using the useActivePath hook in the Navigation component, developers can create a dynamic navigation experience that highlights the active link correctly.

In conclusion, using Next.js 13's App router and the usePathname hook, checking the active navigation link can greatly enhance the user experience in your Next.js applications. By following the above implementation, developers can create a helper function to identify the active navigation link and ensure correct navigation context in their projects.

Top comments (0)