Skip to main content
Wraps <Link> with additional props for styling active and pending states.
import { NavLink } from "react-router";

<NavLink to="/messages">Messages</NavLink>

Type Declaration

export interface NavLinkProps
  extends Omit<LinkProps, "className" | "style" | "children"> {
  children?: React.ReactNode | ((props: NavLinkRenderProps) => React.ReactNode);
  caseSensitive?: boolean;
  className?: string | ((props: NavLinkRenderProps) => string | undefined);
  end?: boolean;
  style?:
    | React.CSSProperties
    | ((props: NavLinkRenderProps) => React.CSSProperties | undefined);
}

export interface NavLinkRenderProps {
  isActive: boolean;
  isPending: boolean;
  isTransitioning: boolean;
}

export const NavLink: React.ForwardRefExoticComponent<
  NavLinkProps & React.RefAttributes<HTMLAnchorElement>
>;

Props

Inherits all props from <Link>, with the following differences:
className
string | ((props: NavLinkRenderProps) => string)
Classes are automatically applied to NavLink that correspond to the state:
a.active { color: red; }
a.pending { color: blue; }
a.transitioning { view-transition-name: my-transition; }
Or specify a function that receives render props:
<NavLink
  to="/messages"
  className={({ isActive, isPending }) =>
    isPending ? "pending" : isActive ? "active" : ""
  }
/>
style
React.CSSProperties | ((props: NavLinkRenderProps) => React.CSSProperties)
Styles can be applied statically or dynamically via a function:
<NavLink to="/messages" style={{ color: "red" }} />

<NavLink
  to="/messages"
  style={({ isActive, isPending }) => ({
    color: isActive ? "red" : isPending ? "blue" : "black",
  })}
/>
children
React.ReactNode | ((props: NavLinkRenderProps) => React.ReactNode)
Can be regular React children or a function that receives render props:
<NavLink to="/messages">
  {({ isActive }) => (
    <span className={isActive ? "active" : ""}>Messages</span>
  )}
</NavLink>
end
boolean
Changes the matching logic for the active and pending states to only match to the “end” of the to path.
LinkURLisActive
<NavLink to="/tasks" />/taskstrue
<NavLink to="/tasks" />/tasks/123true
<NavLink to="/tasks" end />/taskstrue
<NavLink to="/tasks" end />/tasks/123false
<NavLink to="/" /> is an exceptional case because every URL matches /. To avoid this matching every single route by default, it effectively ignores the end prop and only matches when you’re at the root route.
caseSensitive
boolean
Changes the matching logic to make it case-sensitive:
LinkURLisActive
<NavLink to="/SpOnGe-bOB" />/sponge-bobtrue
<NavLink to="/SpOnGe-bOB" caseSensitive />/sponge-bobfalse

Examples

Basic Active Styling

// CSS
.nav a {
  color: black;
}

.nav a.active {
  color: red;
}

// Component
function Nav() {
  return (
    <nav className="nav">
      <NavLink to="/">Home</NavLink>
      <NavLink to="/messages">Messages</NavLink>
      <NavLink to="/tasks">Tasks</NavLink>
    </nav>
  );
}

With className Function

<NavLink
  to="/messages"
  className={({ isActive, isPending, isTransitioning }) => {
    return [
      isPending ? "pending" : "",
      isActive ? "active" : "",
      isTransitioning ? "transitioning" : "",
    ].join(" ");
  }}
>
  Messages
</NavLink>

With style Function

<NavLink
  to="/messages"
  style={({ isActive, isPending }) => ({
    fontWeight: isActive ? "bold" : "",
    color: isPending ? "red" : "black",
  })}
>
  Messages
</NavLink>

With children Function

<NavLink to="/tasks">
  {({ isActive, isPending }) => (
    <span className={isActive ? "active" : ""}>
      Tasks
      {isPending && " (loading...)"}
    </span>
  )}
</NavLink>

Vertical Navigation

function Sidebar() {
  return (
    <nav>
      <NavLink to="dashboard" end>
        <DashboardIcon />
        Dashboard
      </NavLink>
      <NavLink to="dashboard/settings">
        <SettingsIcon />
        Settings
      </NavLink>
      <NavLink to="dashboard/profile">
        <ProfileIcon />
        Profile
      </NavLink>
    </nav>
  );
}
function Breadcrumbs() {
  const matches = useMatches();

  return (
    <ol>
      {matches.map((match, index) => (
        <li key={index}>
          <NavLink to={match.pathname} end>
            {({ isActive }) => (
              <span className={isActive ? "font-bold" : ""}>
                {match.handle?.crumb?.(match.data) || match.pathname}
              </span>
            )}
          </NavLink>
        </li>
      ))}
    </ol>
  );
}

Behavior

  • Automatically applies aria-current="page" to the link when it’s active
  • The isActive state indicates if the link’s URL matches the current location
  • The isPending state is only available in Framework and Data modes and indicates if the pending location matches the link’s URL
  • The isTransitioning state indicates if a view transition to the link’s URL is in progress
  • Default class names (active, pending, transitioning) are applied automatically

Notes

  • isPending is only available when using a data router (Framework or Data mode)
  • Root links (to="/") always use end behavior to avoid matching every route
  • Inherits all other props from <Link> including prefetch, replace, state, etc.

Build docs developers (and LLMs) love