react-router-link-navlink

Link and NavLink in React Router

  • 3 min

In the previous article we configured our Router and defined the routes. Our application already knows what to render based on the URL.

However, we have a (serious) usability problem: we don’t have any buttons to navigate 😊. And if we use traditional HTML links (<a href="...">), we break the SPA behavior.

In this article we are going to learn how to move around our application the right way, using the Link and NavLink components.

The <a> tag

In traditional web development, to go from one page to another we use:

<a href="/contact">Go to Contact</a>
Copied!

Why is it forbidden in React? Because the <a> tag has an unavoidable native behavior: it tells the browser to make a new request to the server.

When this happens:

The browser downloads the HTML again.

All JavaScript restarts.

All React State is lost (useState, useContext, etc).

To solve this, React Router provides us with the <Link> component.

Visually, <Link> renders as an <a> tag in the final DOM (to maintain accessibility and SEO).

But internally, it intercepts the click. It calls event.preventDefault() to prevent reloading and uses the browser’s History API to change the URL manually.

We replace the href attribute with the to prop.

import { Link } from 'react-router';

function Navbar() {
  return (
    <nav>
      {/* ✅ CORRECT WAY */}
      <Link to="/">Home</Link>
      <Link to="/about">About Us</Link>
      <Link to="/contact">Contact</Link>
    </nav>
  );
}
Copied!

When clicking these links, the URL will change instantly and React Router will render the corresponding component without any flickering.

Now, in a navigation bar it is a UX standard to highlight the page we are currently on. We want the “Home” button to appear in a different color if we are on the Home page.

We could do this manually by checking the URL, but React Router gives us a specific component for this: <NavLink>.

<NavLink> is a special version of <Link> that knows if its to prop matches the current browser URL.

The className prop as a function

In modern versions of React Router, the className prop of a NavLink accepts a function. This function receives an object with the isActive property.

import { NavLink } from 'react-router';
import './Navbar.css'; // We assume a .active class { color: red; }

function Navbar() {
  return (
    <nav>
      <NavLink 
        to="/"
        className={({ isActive }) => isActive ? "menu-item active" : "menu-item"}
      >
        Home
      </NavLink>
      
      <NavLink 
        to="/about"
        className={({ isActive }) => isActive ? "menu-item active" : "menu-item"}
      >
        About Us
      </NavLink>
    </nav>
  );
}
Copied!

Relative vs Absolute Routes

An important technical detail about the to prop.

Absolute Route (Starts with /)

<Link to="/contact"> will always take you to domain.com/contact, no matter where we are.

Relative Route (Does NOT start with /)

It is appended to the current URL. If we are on /users and render <Link to="details">, the browser will go to /users/details.

Generally, in the main menu you will use absolute routes.

Complete example: Layout with navigation

Let’s integrate everything we have learned (Routes + Navigation) into a common layout.

// src/App.jsx
import { Routes, Route, NavLink } from 'react-router';
import Home from './pages/Home';
import About from './pages/About';

function App() {
  // Helper function for styles
  const getLinkClass = ({ isActive }) => 
    isActive ? "font-bold text-blue-500" : "text-gray-500";

  return (
    <div>
      {/* 1. Persistent Navigation */}
      <header className="p-4 border-b">
        <nav className="flex gap-4">
          <NavLink to="/" className={getLinkClass}>Home</NavLink>
          <NavLink to="/about" className={getLinkClass}>About</NavLink>
        </nav>
      </header>

      {/* 2. Dynamic Content */}
      <main className="p-4">
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="*" element={<h2>404 Not Found</h2>} />
        </Routes>
      </main>
    </div>
  );
}
Copied!