ReactReact Router: Introduction

React Router: Introduction

A traditional multi-page website loads a fresh HTML document from the server every time the user clicks a link. A Single Page Application (SPA) loads once and then swaps content in and out using JavaScript — no full-page reload, no white flash, no lost scroll position. This is client-side routing, and React Router is the standard library for implementing it in React.

What Client-Side Routing Does
  • Intercepts link clicks — instead of letting the browser navigate, React Router prevents the default behaviour and updates the URL via the History API.

  • Matches the URL to a component tree and renders the matching components.

  • Preserves application state — the React tree stays alive; only the matched components change.

  • Enables deep linking — each view has its own URL that users can bookmark and share.

Installation

Bash
npm install react-router-dom
Minimal Working Router

Here is the smallest complete React Router v6 application. Study the structure — three concepts are doing all the work: BrowserRouter, Routes, and Route:

JSX
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'

function Home()   { return <h1>Home Page</h1> }
function About()  { return <h1>About Us</h1> }
function Contact(){ return <h1>Contact</h1> }

function App() {
  return (
    <BrowserRouter>
      {/* Navigation — renders <a> tags that don't cause page reloads */}
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/contact">Contact</Link>
      </nav>

      {/* Route declarations — only one renders at a time */}
      <Routes>
        <Route path="/"        element={<Home />} />
        <Route path="/about"   element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </BrowserRouter>
  )
}
The Core Building Blocks

<BrowserRouter> — The top-level provider that reads the current URL from the browser's History API and makes it available to all nested components. Wrap your entire app with it once, usually in main.jsx.

<Routes> — The container that looks at the current URL and renders the first matching <Route>. Think of it as a switch statement for URLs. In v6 this replaced <Switch> from v5.

<Route path="..." element={...} /> — Declares that when the URL matches path, React Router should render the element. The element prop accepts JSX (not a component reference), so you can pass props inline: element={<UserPage userId={1} />}.

<Link to="..."> — Renders an anchor tag that updates the URL without a page reload when clicked. Always use <Link> instead of <a href="..."> for internal navigation.

NavLink: Active Link Styling

<NavLink> is a special version of <Link> that knows whether it is currently active. It automatically adds an active CSS class to the rendered anchor when its path matches the current URL:

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

function Nav() {
  return (
    <nav>
      {/* 'active' class added automatically when URL matches */}
      <NavLink to="/" end>Home</NavLink>
      <NavLink to="/about">About</NavLink>
      <NavLink to="/blog">Blog</NavLink>
    </nav>
  )
}

// Custom active style via callback
<NavLink
  to="/about"
  className={({ isActive }) => isActive ? 'link link--active' : 'link'}
  style={({ isActive }) => ({ fontWeight: isActive ? 'bold' : 'normal' })}
>
  About
</NavLink>
Note
The `end` prop on the Home NavLink is important. Without it, `/` would match every URL (since every path starts with /), making the Home link always appear active.
React Router v6 vs v5

Concept

v5

v6

Route container

<Switch>

<Routes>

Route element

component={Home}

element={<Home />}

Nested routes

Manually nested inside components

Declarative in route config

Redirects

<Redirect to="/" />

<Navigate to="/" />

useHistory

useHistory()

useNavigate()

Exact matching

exact prop required

Exact by default

Route ranking

First match wins

Best match wins

A404 / Catch-All Route

JSX
function NotFound() {
  return <h1>404 — Page Not Found</h1>
}

<Routes>
  <Route path="/"      element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="*"      element={<NotFound />} />   {/* matches anything else */}
</Routes>
React Router vs Framework Routing

React Router (SPA)

Next.js (framework)

Remix (framework)

Routing type

Client-side

File-system + hybrid

File-system + nested

SSR support

No (SPA only)

Yes (built-in)

Yes (first-class)

Data loading

Manual + TanStack Query

fetch in RSC / getServerSideProps

loader functions

Bundle

react-router-dom pkg

Built-in

Built-in

Use when

Vite SPA, CRA, custom setup

Marketing sites, full-stack apps

Data-heavy web apps

Warning
Do not use React Router in a Next.js project. Next.js has its own file-system router built in — adding React Router on top creates conflicts. Use Next.js's Link and useRouter instead.
Tip
React Router v6.4+ added data APIs (loaders, actions) that bring Remix-style data fetching to React Router. If you're starting a new project with Vite, consider upgrading to these APIs for cleaner data/route co-location.