NextjsActive Link Styling

Active Link Styling

Almost every navigation bar needs to highlight the link that corresponds to the page the user is currently on — a bold label, an underline, a different background color. The App Router doesn't give <Link> a built-in active prop for this, but Next.js gives you exactly what you need to build it yourself: the usePathname() hook from next/navigation, which returns the current URL's path as a plain string on every render.
Note
This page assumes you're already comfortable with the <Link> component and basic navigation. If not, visit the Linking & Navigation and usePathname pages first — this page focuses specifically on the "is this the active route?" pattern built on top of it.
The core idea: compare pathname to href
Once you have the current pathname as a string, "is this link active?" becomes a simple string comparison against each link's href. Wrap that comparison in a small helper component so every nav item gets consistent behavior.

components/NavLink.tsx

TSX
'use client'

import Link from 'next/link'
import { usePathname } from 'next/navigation'

type NavLinkProps = {
  href: string
  children: React.ReactNode
}

export default function NavLink({ href, children }: NavLinkProps) {
  const pathname = usePathname()
  const isActive = pathname === href

  return (
    <Link
      href={href}
      // aria-current tells assistive tech this is the current page,
      // and doubles as a CSS hook: a[aria-current="page"] { ... }
      aria-current={isActive ? 'page' : undefined}
      className={isActive ? 'nav-link nav-link--active' : 'nav-link'}
    >
      {children}
    </Link>
  )
}

A full navigation bar just renders a handful of these, each pointing at a different route:

components/NavBar.tsx

TSX
import NavLink from './NavLink'

export default function NavBar() {
  return (
    <nav>
      <NavLink href="/">Home</NavLink>
      <NavLink href="/blog">Blog</NavLink>
      <NavLink href="/about">About</NavLink>
    </nav>
  )
}
Exact match vs. nested-route match
Strict equality (pathname === href) works well for top-level links like Home or About, but it breaks down for section links that should stay "active" while the user is anywhere inside that section. If the user navigates to /blog/my-first-post, the Blog link's href is /blog — not an exact match — but you probably still want it highlighted. For that, switch to a prefix check with pathname.startsWith(href).
A naive startsWith check matches too broadly
pathname.startsWith(href) compares raw strings, not path segments. If you have a /blog link and the site also has a completely unrelated /blog-archive route, then visiting /blog-archive makes "/blog-archive".startsWith("/blog") evaluate to true — the Blog link lights up on a page it has nothing to do with. The fix is to also check that the character right after the prefix is a path separator (or that there's nothing after it at all).

components/NavLink.tsx — segment-safe matching

TSX
'use client'

import Link from 'next/link'
import { usePathname } from 'next/navigation'

type NavLinkProps = {
  href: string
  children: React.ReactNode
  exact?: boolean
}

export default function NavLink({ href, children, exact }: NavLinkProps) {
  const pathname = usePathname()

  const isActive = exact
    ? pathname === href
    : pathname === href || pathname.startsWith(`${href}/`)

  return (
    <Link
      href={href}
      aria-current={isActive ? 'page' : undefined}
      className={isActive ? 'nav-link nav-link--active' : 'nav-link'}
    >
      {children}
    </Link>
  )
}
Appending a trailing slash before comparing (`${href}/` as the prefix) guarantees the match only succeeds at a real path boundary — /blog/ is a prefix of /blog/my-first-post, but it is not a prefix of /blog-archive. Use the exact prop for links that should only ever match their own exact route, like Home pointing at / (where a prefix check would match literally every route on the site).
Note
This pattern requires a Client Component. usePathname() is a hook, and hooks only run in components that opt into the client with 'use client' — there is no server-side equivalent, because "the current route the browser is displaying" is inherently client-side navigation state. In practice this is fine: keep the nav link itself small and client-side while the rest of the page around it stays a Server Component.
  • usePathname() returns the current URL path as a plain string, re-evaluated on every navigation.

  • Exact match (pathname === href) suits top-level links; prefix match suits section links with nested routes.

  • Guard prefix matches at a path boundary (e.g. compare against `${href}/`) to avoid false positives like /blog matching /blog-archive.

  • usePathname is a hook, so any component using it must be a Client Component.