How to Implement an Active NavLink with React Router: A Complete Guide
Use the NavLink component from react-router-dom and provide a function to the className or style prop that receives { isActive } to dynamically apply styling when the link matches the current route.
Implementing an active NavLink with React Router is essential for creating intuitive navigation interfaces that visually indicate the user's current location. The react-router-dom package provides a specialized NavLink component that automatically handles route matching and active state detection, eliminating the need for manual pathname comparisons in most cases. Unlike the standard Link component, NavLink is designed specifically for navigation menus where visual feedback improves user experience.
Understanding the NavLink Component
The NavLink component extends the standard Link component with built-in active state detection. According to the react-router-dom source code (located in packages/react-router-dom/NavLink.tsx), the component works by comparing the resolved URL of the link to the browser's current pathname. When they match, NavLink automatically injects either a CSS class (defaulting to "active"), a custom class, or a style object that you provide.
Core Props for Active Navigation
The className Function Prop
The className prop accepts either a static string or a function that receives navigation data and returns a string. This function receives an object containing isActive and isPending boolean flags, allowing you to construct dynamic class names based on the current route state.
import { NavLink } from "react-router-dom";
<NavLink
to="/dashboard"
className={({ isActive }) => (isActive ? "nav-link active" : "nav-link")}
>
Dashboard
</NavLink>
The style Function Prop
Similar to className, the style prop can accept a function that returns a CSSProperties object. This approach is ideal for inline styling or when using CSS-in-JS solutions where you need to programmatically adjust styles based on the active state.
import { NavLink } from "react-router-dom";
<NavLink
to="/notifications"
style={({ isActive }) => ({
color: isActive ? "#ff6600" : "#333",
fontWeight: isActive ? "600" : "400",
})}
>
Notifications
</NavLink>
Exact Matching with the end Prop
By default, NavLink considers a link active when the current pathname starts with the link's path. For root routes or when you need exact matching, use the end prop. When end is true, the link is only active when the pathname exactly matches the to prop value.
<NavLink to="/" end>
Home (active only on exact "/")
</NavLink>
Implementation Examples
Basic Active Class Implementation
For simple navigation menus, you can rely on the default "active" class that NavLink automatically applies. Define your CSS to style the active state, and let the component handle the rest.
import { NavLink } from "react-router-dom";
function MainMenu() {
return (
<nav>
<NavLink to="/" end>Home</NavLink>
<NavLink to="/about">About</NavLink>
<NavLink to="/contact">Contact</NavLink>
</nav>
);
}
.nav-link { padding: 8px; color: #555; }
.nav-link.active { font-weight: bold; color: #000; }
Dynamic Styling with CSS Modules
When using CSS Modules or scoped styling, use the function form of className to return the appropriate class based on isActive.
import { NavLink } from "react-router-dom";
import styles from "./Sidebar.module.css";
function Sidebar() {
return (
<aside>
<NavLink
to="/settings"
className={({ isActive }) =>
isActive ? styles.selected : styles.item
}
>
Settings
</NavLink>
</aside>
);
}
Custom Active Logic with useLocation
For complex matching requirements—such as considering query strings, hash fragments, or custom patterns—you can compute the active state manually using the useLocation hook and pass the result to NavLink or a styled wrapper.
import { NavLink, useLocation } from "react-router-dom";
function MyNavLink({ to, children }) {
const location = useLocation();
const isActive = location.pathname === to; // simple exact match
return (
<NavLink
to={to}
className={isActive ? "active-link" : undefined}
>
{children}
</NavLink>
);
}
Accessibility Considerations
NavLink automatically manages the aria-current attribute for active links, setting it to "page" by default when the link is active. This signals to screen readers that the link represents the current page, which is essential for users navigating with assistive technologies. You can customize this behavior using the ariaCurrent prop if your design requires a different value, though "page" is the standard for navigation menus.
Summary
- Use
NavLinkinstead ofLinkwhen you need visual indication of the current route in navigation menus. - Leverage function props for
classNameandstyleto dynamically apply styling based on theisActiveboolean. - Apply the
endprop for root routes or when you need exact matching rather than prefix matching. - Rely on built-in accessibility features like automatic
aria-current="page"attributes for screen reader support. - Reference the source in
react-router-dom(specifically theNavLinkcomponent implementation inpackages/react-router-dom/NavLink.tsx) for advanced customization patterns.
Frequently Asked Questions
What is the difference between Link and NavLink in React Router?
Link is the fundamental component for creating navigation anchors in React Router, rendering a standard accessible anchor tag that updates the URL when clicked. NavLink extends Link with active state detection, automatically applying styling classes or inline styles when the link's route matches the current location, making it ideal for navigation menus where you need to highlight the current page.
How do I make a NavLink active only on exact match?
Use the end prop on your NavLink component. By default, NavLink uses partial matching, meaning /dashboard would be considered active for both /dashboard and /dashboard/settings. When you add end={true}, the link only receives the active state when the current pathname exactly matches the to prop value, which is particularly important for root home routes (to="/").
Can I use NavLink with CSS Modules or Tailwind CSS?
Yes, NavLink works seamlessly with CSS Modules, Tailwind CSS, and any CSS-in-JS solution. Use the function form of the className prop to return the appropriate class names based on the isActive state. For Tailwind, you might return conditional strings like isActive ? "bg-blue-500 text-white" : "text-gray-600", while for CSS Modules, you would return isActive ? styles.active : styles.link.
Why is my NavLink not showing as active?
The most common cause is a mismatch between the to prop value and the current pathname, particularly with trailing slashes or partial matching behavior. Ensure your route paths match exactly, or use the end prop if you need exact matching. Also verify that your CSS selectors are correctly targeting the .active class (or your custom class) and that no other styles are overriding them. If using the function form of className, debug by logging the isActive value to ensure it matches your expectations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →