Skip to main content

React Router: Active Navigation Links with NavLink and Styling

Navigation is central to any multi-page application. The <NavLink> component from React Router extends the basic <Link> by automatically adding an active class when its path matches the current URL. This enables you to highlight the current page in your navigation menu — a critical UX feature that tells users where they are. This article walks through building a complete navigation menu with active link styling, visual feedback, and best practices for modern routing.

Key Takeaways

  • <NavLink> is route-aware — unlike <Link>, it automatically detects when its path matches the current route and applies an active class.
  • Active class is applied automatically — when a <NavLink> path matches the current URL, React Router adds the active class to the element, allowing CSS to style it differently.
  • Styling active links — use CSS to target the .active class: change text color, font weight, add a background color, border, or any visual indication of the current page.
  • Current best practice — use the className prop as a function to conditionally apply classes based on isActive status (v6+ syntax) for more control.
  • Navigation menu pattern — wrap <NavLink> elements in a <nav> element and semantically use <ul> / <li> for accessible, maintainable menus.

Both <Link> and <NavLink> navigate to a new route without a full page reload. The key difference: <NavLink> is aware of the current route.

  • <Link> — basic client-side navigation. Does not know or care about the current route.
  • <NavLink> — knows the current route and automatically applies the active class when its to prop matches the URL.
import { Link, NavLink } from 'react-router-dom';

// Link: no indication of active state
<Link to="/about">About</Link>

// NavLink: automatically gets .active class when route is /about
<NavLink to="/about">About</NavLink>

When the user visits /about, React Router automatically adds the active class to the <NavLink to="/about"> element. You then use CSS to style it.

Building a navigation menu involves three steps: creating the <NavLink> components, styling them with CSS, and integrating the menu into your app.

Step 1: Create a Navbar Component

import React from 'react';
import { NavLink } from 'react-router-dom';
import './Navbar.css';

function Navbar() {
return (
<nav className="navbar">
<ul className="nav-links">
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/about">About</NavLink>
</li>
<li>
<NavLink to="/services">Services</NavLink>
</li>
<li>
<NavLink to="/contact">Contact</NavLink>
</li>
</ul>
</nav>
);
}

export default Navbar;

Use semantic HTML: wrap links in a <nav> element, and structure the list with <ul> and <li> for accessibility. Each <NavLink to="/path"> creates a link that becomes active when the URL matches /path.

/* Navbar.css */

nav.navbar {
background-color: #f8f9fa;
padding: 1rem 2rem;
border-bottom: 1px solid #dee2e6;
}

.nav-links {
list-style: none;
display: flex;
gap: 2rem;
margin: 0;
padding: 0;
}

nav a {
text-decoration: none;
color: #333;
font-weight: 500;
padding: 0.5rem 1rem;
border-radius: 4px;
transition: all 0.3s ease;
}

/* Style the active link */
nav a.active {
color: #007bff;
font-weight: 700;
background-color: #e7f3ff;
border-left: 3px solid #007bff;
}

/* Hover effect for non-active links */
nav a:not(.active):hover {
color: #0056b3;
background-color: #f0f0f0;
}

The .active selector targets the link when its class is active. This allows visual differentiation: change text color, add a background color, add a border, increase font weight, or all of the above.

Step 3: Integrate the Navbar into Your App

import React from 'react';
import { Routes, Route } from 'react-router-dom';
import Navbar from './components/Navbar';
import Home from './pages/Home';
import About from './pages/About';
import Services from './pages/Services';
import Contact from './pages/Contact';

function App() {
return (
<div>
<Navbar />
<main>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/services" element={<Services />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</main>
</div>
);
}

export default App;

Place the <Navbar> component above the <Routes> so it appears on every page. When the user navigates, the matching <NavLink> automatically becomes active, and CSS styles it.

React Router v6 provides a more flexible syntax: pass a function to className that receives an { isActive } object, allowing you to conditionally apply classes or inline styles.

import { NavLink } from 'react-router-dom';

function Navbar() {
return (
<nav>
<NavLink
to="/about"
className={({ isActive }) => isActive ? 'nav-link active' : 'nav-link'}
>
About
</NavLink>
</nav>
);
}

Or use a helper function for cleaner syntax:

const getLinkClass = ({ isActive }) => 
isActive ? 'nav-link nav-link--active' : 'nav-link';

<NavLink to="/about" className={getLinkClass}>
About
</NavLink>

This gives you full control: you can add multiple classes, apply inline styles, or even swap entire components based on whether the link is active.

Underline Indicator

nav a {
position: relative;
text-decoration: none;
}

nav a.active::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
width: 100%;
height: 2px;
background-color: #007bff;
}

Left Border Accent

nav a.active {
border-left: 3px solid #007bff;
padding-left: calc(1rem - 3px);
}

Badge or Pill Style

nav a.active {
background-color: #007bff;
color: white;
border-radius: 20px;
padding: 0.5rem 1rem;
}

Subtle Background Highlight

nav a.active {
background-color: #f0f0f0;
font-weight: 600;
}

Choose a pattern that fits your design. The key is clarity: users must immediately see which page they are on.

What Are Best Practices for Navigation Menus?

Use Semantic HTML

<nav>              {/* <nav> landmark region */}
<ul> {/* List of links */}
<li>
<NavLink to="/">Home</NavLink>
</li>
</ul>
</nav>

Screen readers and accessibility tools understand this structure. Never use <div> instead of <nav>.

Wrapping each <NavLink> in an <li> improves semantics and makes CSS styling (like spacing between items) cleaner.

Make links at least 44×44 CSS pixels for touch accessibility:

nav a {
display: inline-block;
padding: 0.75rem 1rem; /* Ensures adequate clickable area */
min-height: 44px;
display: flex;
align-items: center;
}

Test Active State on All Pages

Verify the active styling appears correctly on every route. A common bug: the NavLink path is /about but the route is /About (case-sensitive).

Frequently Asked Questions

Check that the to prop exactly matches the route path. React Router is case-sensitive. /about and /About are different.

// Route definition
<Route path="/about" element={<About />} />

// NavLink (must match exactly)
<NavLink to="/about">About</NavLink>

Yes, use the className function syntax:

<NavLink
to="/about"
style={({ isActive }) => ({
color: isActive ? '#007bff' : '#333',
fontWeight: isActive ? 'bold' : 'normal',
})}
>
About
</NavLink>

However, CSS classes are usually cleaner and more maintainable.

How do I style a partial match (e.g., all /admin/* routes)?

Use the end prop. By default, /admin is active for both /admin and /admin/users. Add end to match only exact paths:

<NavLink to="/admin" end>Admin</NavLink>      {/* Active only at /admin */}
<NavLink to="/admin">Admin</NavLink> {/* Active at /admin and /admin/* */}

Use <NavLink> in navigation menus where showing the current page is important. Use <Link> for contextual links in page content (like "Read more" or inline references), where active state is not relevant.

Use the className function to check if any child route is active:

const isAdminActive = ({ isActive }) => 
isActive || location.pathname.startsWith('/admin');

<NavLink to="/admin" className={isAdminActive ? 'active' : ''}>
Admin
</NavLink>

Or use React Router's useResolvedPath and useLocation hooks for more control.

Further Reading