React Router

React Router v6 turns a single-page React app into a multi-page experience by mapping URL paths to components without full page reloads.

Setting Up the Router

Everything react-router-dom does starts with a router component wrapping your app. BrowserRouter uses the HTML5 History API, so the address bar shows real, shareable, bookmarkable URLs while React swaps components in and out under the hood — no full-page reload ever happens.

Defining Routes

Inside the router, a Routes component holds one or more Route elements. Each Route pairs a path with the element to render when the current URL matches that path. Routes scans its children top to bottom and renders only the first match, so order matters — more specific paths should come before catch-alls.

  • BrowserRouter — wraps the app and syncs it with the browser's URL
  • Routes — picks the single best-matching Route from its children
  • Route — maps one path to one element
  • Link — navigates without a full page reload
  • NavLink — a Link that knows whether it points at the active URL
  • useNavigate — navigate programmatically from event handlers or effects
  • useParams — read dynamic segments (like :id) out of the current URL

Basic route setup

// NOTE: This example needs the "react-router-dom" package, which is not
// installed in this sandbox by default. Add it as a dependency to run this
// example as-is.
import { BrowserRouter, Routes, Route } from "react-router-dom";

// Home/About/Contact would normally live in their own files and be
// imported (import Home from "./Home"; etc.). They're defined inline here
// since this sandbox only runs a single file.
function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Contact() {
  return <h2>Contact</h2>;
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
        <Route path="*" element={<h1>404: Page Not Found</h1>} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Use Link (or NavLink) instead of a plain anchor tag for in-app navigation. A regular <a href> forces the browser to request a brand-new document from the server; Link intercepts the click, updates the URL via the History API, and lets React re-render only the parts of the page that changed.

Link and NavLink

// NOTE: This example needs the "react-router-dom" package, which is not
// installed in this sandbox by default. Add it as a dependency to run this
// example as-is.
import { BrowserRouter, Link, NavLink } from "react-router-dom";

function NavBar() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <NavLink
        to="/about"
        className={({ isActive }) => (isActive ? "link active" : "link")}
      >
        About
      </NavLink>
      <NavLink to="/contact">Contact</NavLink>
    </nav>
  );
}

// Link and NavLink must render inside a Router, so this wraps NavBar with
// BrowserRouter to make the example runnable on its own.
function App() {
  return (
    <BrowserRouter>
      <NavBar />
    </BrowserRouter>
  );
}

export default App;
Note: Nested routes with an <Outlet /> let a parent route render shared layout (like a sidebar or navbar) while child routes swap only the inner content — no need to repeat the layout in every page component.

Programmatic navigation with useNavigate

// NOTE: This example needs the "react-router-dom" package, which is not
// installed in this sandbox by default. Add it as a dependency to run this
// example as-is.
import { MemoryRouter, Routes, Route, useNavigate, useParams } from "react-router-dom";

function UserProfile() {
  const { userId } = useParams();
  const navigate = useNavigate();

  function handleLogout() {
    navigate("/login", { replace: true });
  }

  return (
    <div>
      <h2>Profile for user {userId}</h2>
      <button onClick={handleLogout}>Log out</button>
      <button onClick={() => navigate(-1)}>Go back</button>
    </div>
  );
}

function LoginPage() {
  return <h2>Logged out</h2>;
}

// useNavigate/useParams need routing context. MemoryRouter with a fixed
// initial entry (instead of BrowserRouter) makes the /user/:userId param
// available immediately without depending on the sandbox's real URL.
function App() {
  return (
    <MemoryRouter initialEntries={["/user/42"]}>
      <Routes>
        <Route path="/user/:userId" element={<UserProfile />} />
        <Route path="/login" element={<LoginPage />} />
      </Routes>
    </MemoryRouter>
  );
}

export default App;
NameTypePurpose
BrowserRouterComponentWraps the app and syncs the UI with the URL using the History API
RoutesComponentRenders the first child Route that matches the current URL
RouteComponentMaps one path to one element
LinkComponentRenders an accessible <a> without a full page reload
NavLinkComponentLike Link, but knows when it is currently active
useNavigateHookReturns a function to navigate programmatically
useParamsHookReads dynamic segments from the current URL
Note: React Router v6 replaced <Switch> with <Routes> and dropped the component/render props on <Route> in favor of a single element prop — code written for v5 will not run unmodified.

Exercise: React Router

Why use <Link> instead of a plain <a> tag for in-app navigation?