Highlight Currently Active Link in Next.js 13 with App Router
Tags: nextjs, react, tutorial, beginners
Published: 2023-06-12 08:00:00

> Also available on my [blog](https://nikolasbarwicki.com/articles/highlight-currently-active-link-in-nextjs-13-with-app-router)

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](https://nextjs.org/docs/app), 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:

```tsx
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:

```tsx
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 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:

```tsx
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**.

```tsx
import { usePathname } from 'next/navigation'

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

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

  return isActivePath
}
```

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.

```tsx
"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>
  )
}
```

## 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:

```tsx
import { usePathname } from 'next/navigation'

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

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

  return isActivePath
}
```

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:

```tsx
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
}
```

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:

```tsx
// 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
}
```

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

```tsx
'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>
  )
}
```

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.