skip to content
Nikolas Barwicki - Javascript Blog Nikolas's Blog

Highlight Active Link in Next.js 14 with App Router

/ 5 min read

Last Updated:

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 14 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.

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' },
]

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

Creating a basic useActivePath 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 14 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'

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 checkActivePath = (path: string) => {
    return path === pathname
  }

  return checkActivePath
}

The checkActivePath 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 checkActivePath = useActivePath()

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

Handling nested paths and the root path issue

The initial implementation of our checkActivePath 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 checkActivePath 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 checkActivePath = (path: string) => {
    return pathname.startsWith(path)
  }

  return checkActivePath
}

Now, the checkActivePath 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 checkActivePath function:

import { usePathname } from 'next/navigation'

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

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

  return checkActivePath
}

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 14 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 checkActivePath = (path: string) => {
    if (path === '/' && pathname !== path) {
      return false
    }
    return pathname.startsWith(path)
  }

  return checkActivePath
}

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 checkActivePath = useActivePath()

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

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 14’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.