# Client-Side Routing

Course: [React](https://www.devtutorials.ai/learn/react) · Section: Routing
Source: https://www.devtutorials.ai/learn/react/routing/client-side-routing

> Routing with React Router: BrowserRouter, Routes and Route, Link and NavLink, useNavigate, route and query parameters, nested and index routes, layouts and a Not Found page.

## Introduction to Routing

**Routing** means showing a different page for each web address (URL). For example, `/books` shows the list of books, and `/about` shows the About page.

In a React app, routing happens **in the browser**. This is called **client-side routing**. When you click a link, React changes the address and swaps the component on the screen. The page does not reload.

### Real-World Example

A bookshop app has the URLs `/`, `/books` and `/books/42`. React Router reads the URL and shows `Home`, `BookList` or `BookDetails`.

A user clicks a `<Link to="/books/42">`. The router changes the URL and shows the details of book 42 without a page reload. The user can bookmark this URL or share it.

<figure className="react-fig">
<svg viewBox="0 0 520 170" role="img" aria-labelledby="fig-r-routing" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-r-routing">Client-side routing: the address changes, and React shows a different component, without reloading the page</title>
  <rect x="60" y="10" width="400" height="150" rx="10" style={{ fill: "var(--panel2)", stroke: "var(--line)", strokeWidth: 1.5 }} />
  <g opacity="1">
    <rect x="80" y="20" width="360" height="26" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
    <text x="260.0" y="38.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>bookcorner.com/</text>
    <animate attributeName="opacity" values="1;0;0;1" keyTimes="0.000;0.333;0.667;1" calcMode="discrete" dur="6s" repeatCount="indefinite" />
  </g>
  <g opacity="0">
    <rect x="80" y="20" width="360" height="26" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
    <text x="260.0" y="38.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>bookcorner.com/books</text>
    <animate attributeName="opacity" values="0;1;0;0" keyTimes="0.000;0.333;0.667;1" calcMode="discrete" dur="6s" repeatCount="indefinite" />
  </g>
  <g opacity="0">
    <rect x="80" y="20" width="360" height="26" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
    <text x="260.0" y="38.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>bookcorner.com/about</text>
    <animate attributeName="opacity" values="0;0;1;0" keyTimes="0.000;0.333;0.667;1" calcMode="discrete" dur="6s" repeatCount="indefinite" />
  </g>
  <g opacity="1">
    <rect x="80" y="62" width="360" height="84" rx="8" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)", strokeWidth: 1.5 }} />
    <text x="260.0" y="109.0" textAnchor="middle" style={{ fill: "var(--accent)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>&lt;HomePage /&gt;</text>
    <animate attributeName="opacity" values="1;0;0;1" keyTimes="0.000;0.333;0.667;1" calcMode="discrete" dur="6s" repeatCount="indefinite" />
  </g>
  <g opacity="0">
    <rect x="80" y="62" width="360" height="84" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
    <text x="260.0" y="109.0" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>&lt;BooksPage /&gt;</text>
    <animate attributeName="opacity" values="0;1;0;0" keyTimes="0.000;0.333;0.667;1" calcMode="discrete" dur="6s" repeatCount="indefinite" />
  </g>
  <g opacity="0">
    <rect x="80" y="62" width="360" height="84" rx="8" style={{ fill: "var(--ok-soft)", stroke: "var(--ok)", strokeWidth: 1.5 }} />
    <text x="260.0" y="109.0" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>&lt;AboutPage /&gt;</text>
    <animate attributeName="opacity" values="0;0;1;0" keyTimes="0.000;0.333;0.667;1" calcMode="discrete" dur="6s" repeatCount="indefinite" />
  </g>
</svg>
<figcaption>One page in the browser. Only the part that matches the address is swapped.</figcaption>
</figure>

A React app is one HTML page. The router reads the URL and decides which component to show.

## Setting Up React Router

React does not have routing built in. The most used routing library is **React Router**. Its package for websites is called `react-router-dom`.

Install it in your project with one command:

```bash
npm install react-router-dom
```

Then you import the parts you need from `"react-router-dom"`, such as `BrowserRouter`, `Routes`, `Route` and `Link`. This lesson uses React Router version 6.

The code boxes on this page already have React Router installed. Each box shows a small address bar at the top of the preview, with Back and Forward buttons, so you can see the URL change.

## BrowserRouter

`BrowserRouter` is the component that turns routing on. You wrap your whole app in it, once, usually in `main.tsx` or at the top of `App`.

```tsx
import { BrowserRouter } from "react-router-dom";

createRoot(document.getElementById("root")!).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);
```

`BrowserRouter` keeps the app in step with the browser's address bar. It also makes the browser's Back and Forward buttons work.

All other router parts, such as `Routes`, `Link` and `useNavigate`, only work **inside** `BrowserRouter`.

The code boxes on this page cannot change your real address bar. So inside them, `BrowserRouter` keeps the address in memory, and draws a small address bar at the top of the preview. Your code is the same as in a real app.

## Routes and Route Components

You list your pages with two components:

- **`<Routes>`** — a container that holds all your routes. It shows only the **one** route that matches the URL.
- **`<Route>`** — one page. `path` is the address, and `element` is the component to show.

```tsx
<Routes>
  <Route path="/" element={<HomePage />} />
  <Route path="/books" element={<BooksPage />} />
  <Route path="/about" element={<AboutPage />} />
</Routes>
```

<figure className="react-fig">
<svg viewBox="0 0 520 150" role="img" aria-labelledby="fig-r-match" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-r-match">The URL is compared with each Route path. The matching Route's element is shown</title>
  <rect x="5" y="50" width="120" height="46" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="65.0" y="70.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>/books</text>
  <text x="65.0" y="87.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>the URL now</text>
  <path d="M125 73 L165 73" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M165 73 L156.7 76.5 L156.7 69.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="170" y="8" width="170" height="134" rx="10" style={{ fill: "var(--panel2)", stroke: "var(--faint)" }} />
  <text x="255" y="26" textAnchor="middle" style={{ fill: "var(--muted)", fontSize: 12, fontWeight: 700, fontFamily: "var(--mono)" }}>&lt;Routes&gt;</text>
  <rect x="182" y="36" width="146" height="28" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="255.0" y="55.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>path="/"</text>
  <rect x="182" y="70" width="146" height="28" rx="8" style={{ fill: "var(--ok-soft)", stroke: "var(--ok)", strokeWidth: 1.5 }} />
  <text x="255.0" y="89.0" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>path="/books"</text>
  <rect x="182" y="104" width="146" height="28" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="255.0" y="123.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>path="/about"</text>
  <path d="M340 84 L380 84" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M380 84 L371.7 87.5 L371.7 80.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="385" y="60" width="130" height="48" rx="8" style={{ fill: "var(--ok-soft)", stroke: "var(--ok)", strokeWidth: 1.5 }} />
  <text x="450.0" y="81.0" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>&lt;BooksPage /&gt;</text>
  <text x="450.0" y="98.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>shown</text>
  <circle r="6" style={{ fill: "var(--accent-secondary)" }}>
    <animateMotion dur="2s" repeatCount="indefinite" path="M125 73 L165 73 M340 84 L380 84" />
  </circle>
</svg>
<figcaption>Routes looks for the Route whose path matches the URL, and shows its element.</figcaption>
</figure>

## Navigation with Link

`<Link>` makes a clickable link to another route. Use `to` for the address.

```tsx
<Link to="/books">Books</Link>
```

Do not use a normal `<a href="/books">` inside a React app. A normal link reloads the whole page, so all your state is lost. `Link` changes the page without reloading.

      <nav>
        <Link to="/">Home</Link> | <Link to="/books">Books</Link> | <Link to="/about">About</Link>
      </nav>

        <Route path="/" element={<HomePage />} />
        <Route path="/books" element={<BooksPage />} />
        <Route path="/about" element={<AboutPage />} />
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click the links. The address bar at the top changes, and only the heading below the menu changes. The menu stays, because it is outside `<Routes>`.

Now press the **←** Back button in the address bar. You go back to the page before, just like in a real browser.

## NavLink

`NavLink` is a special `Link` that knows when its page is **open**. It gives itself the class `active` on the current page. You can use that class to highlight the menu item.

      <nav className="menu">
        <NavLink to="/" end>Home</NavLink>
        <NavLink to="/books">Books</NavLink>
        <NavLink to="/offers">Offers</NavLink>
      </nav>

        <Route path="/" element={<h2>Home</h2>} />
        <Route path="/books" element={<h2>Books</h2>} />
        <Route path="/offers" element={<h2>Offers: 20% off this week</h2>} />
  );
}
`,
    "App.css": `.menu { display: flex; gap: 6px; }
.menu a { padding: 4px 12px; border-radius: 999px; text-decoration: none; color: #334155; background: #f1f5f9; }
.menu a.active { background: #0047ba; color: #fff; font-weight: 600; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click each menu item. The blue one is always the page you are on.

Look at `end` on the Home link. Without it, `/` would count as active on every page, because every address starts with `/`. `end` means: active only when the address is exactly `/`.

## useNavigate Hook

Sometimes you want to change the page **from code**, not from a link. For example, go to a "Thank you" page after an order is placed.

`useNavigate` gives you a function, usually called `navigate`. Call it with an address.

```tsx
const navigate = useNavigate();
navigate("/thank-you"); // go to this page
navigate(-1);           // go back one page
```

      <h2>Your cart</h2>
      <p>Atomic Habits · ₹499</p>
      <button onClick={placeOrder}>Place order</button>
    </div>
  );
}

function ThankYouPage() {
  const navigate = useNavigate();
  return (
    <div>
      <h2>Thank you for your order!</h2>
      <button onClick={() => navigate("/")}>Back to the cart</button>
    </div>
  );
}

  return (
        <Route path="/" element={<CartPage />} />
        <Route path="/thank-you" element={<ThankYouPage />} />
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click **Place order**. The code runs first (the console shows a message), and then `navigate` opens the thank-you page.

Use `Link` when the user clicks to go somewhere. Use `navigate` when your code decides to go somewhere, such as after a form is sent.

## Route Parameters

A **route parameter** is a changing part of the address, such as the id of a book: `/books/1`, `/books/2`, `/books/3`.

You write one route with a `:` before the changing part. Then you read it with the `useParams` hook.

<figure className="react-fig">
<svg viewBox="0 0 520 110" role="img" aria-labelledby="fig-r-params" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-r-params">Route parameter: the path /books/:id matches /books/42, and useParams gives id = 42</title>
  <rect x="5" y="30" width="150" height="50" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="80.0" y="52.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>/books/42</text>
  <text x="80.0" y="69.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>the URL</text>
  <path d="M155 55 L190 55" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M190 55 L181.7 58.5 L181.7 51.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="195" y="30" width="160" height="50" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
  <text x="275.0" y="52.0" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>/books/:id</text>
  <text x="275.0" y="69.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>the Route path</text>
  <path d="M355 55 L390 55" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M390 55 L381.7 58.5 L381.7 51.5 Z" style={{ fill: "var(--faint)" }} />
  <rect x="395" y="30" width="120" height="50" rx="8" style={{ fill: "var(--ok-soft)", stroke: "var(--ok)", strokeWidth: 1.5 }} />
  <text x="455.0" y="52.0" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>useParams()</text>
  <text x="455.0" y="69.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 11, fontFamily: "var(--sans)" }}>&#123; id: "42" &#125;</text>
  <circle r="6" style={{ fill: "var(--accent-secondary)" }}>
    <animateMotion dur="2s" repeatCount="indefinite" path="M155 55 L190 55 M355 55 L390 55" />
  </circle>
</svg>
<figcaption>The :id part of the path catches any value, and useParams gives it to you.</figcaption>
</figure>

      {books.map((book) => (
        <li key={book.id}>
          <Link to={"/books/" + book.id}>{book.title}</Link>
        </li>
      ))}
    </ul>
  );
}

function BookDetails() {
  const { id } = useParams();
  const book = books.find((b) => b.id === id);

  if (!book) return <p>Sorry, no book has the id {id}.</p>;

  return (
    <div>
      <h2>{book.title}</h2>
      <p>by {book.author} · ₹{book.price}</p>
      <Link to="/">Back to all books</Link>
    </div>
  );
}

  return (
        <Route path="/" element={<BookList />} />
        <Route path="/books/:id" element={<BookDetails />} />
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click a book. The address becomes `/books/2`, for example. `BookDetails` reads `id` with `useParams`, and finds the book with that id.

Look at this line:

```tsx
const { id } = useParams();
```

The name `id` matches `:id` in the route path. A parameter is always **text**, so the ids in `books.ts` are text too: `"2"`, not `2`.

## Query Parameters

**Query parameters** are extra settings at the end of the address, after a `?`. For example: `/search?q=habits&sort=price`.

They are used for things like search words, filters and sorting. They do not change which route matches. You read and change them with `useSearchParams`.

<figure className="react-fig">
<svg viewBox="0 0 520 110" role="img" aria-labelledby="fig-r-query" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-r-query">Query parameters: the part after ? holds key=value pairs, read with useSearchParams</title>
  <text x="260" y="34" textAnchor="middle" style={{ fontSize: 16, fontFamily: "var(--mono)" }}><tspan style={{ fill: "var(--ink2)" }}>/search</tspan><tspan style={{ fill: "var(--accent-secondary)", fontWeight: 700 }}>?</tspan><tspan style={{ fill: "var(--accent)", fontWeight: 700 }}>q=habits</tspan><tspan style={{ fill: "var(--accent-secondary)", fontWeight: 700 }}>&amp;</tspan><tspan style={{ fill: "var(--ok)", fontWeight: 700 }}>sort=price</tspan></text>
  <text x="205" y="70" textAnchor="middle" style={{ fill: "var(--muted)", fontSize: 11, fontWeight: 400, fontFamily: "var(--sans)" }}>path</text>
  <text x="270" y="70" textAnchor="middle" style={{ fill: "var(--accent)", fontSize: 12, fontWeight: 600, fontFamily: "var(--mono)" }}>q = "habits"</text>
  <text x="400" y="70" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 12, fontWeight: 600, fontFamily: "var(--mono)" }}>sort = "price"</text>
  <text x="260" y="98" textAnchor="middle" style={{ fill: "var(--muted)", fontSize: 11, fontWeight: 400, fontFamily: "var(--sans)" }}>? starts the list · &amp; joins two pairs</text>
</svg>
<figcaption>Query parameters are optional settings after the ?. They do not change which Route matches.</figcaption>
</figure>

      <input
        placeholder="Search books"
        value={q}
        onChange={(e) => setSearchParams({ q: e.target.value })}
      />
      <ul>
        {results.map((b) => <li key={b}>{b}</li>)}
      </ul>
    </div>
  );
}

  return (
        <Route path="/" element={<SearchPage />} />
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Type `in` in the box. The address becomes `/?q=in`, and the list shows only matching books. Because the search is in the address, a user can copy the link and share the same search.

- `searchParams.get("q")` reads the value of `q`. It gives `null` if there is no `q`, so `?? ""` uses an empty text instead.
- `setSearchParams({ q: "..." })` changes the query part of the address.

## Nested Routes

A **nested route** is a route inside another route. The child's path is added to the parent's path.

```tsx
<Route path="/account" element={<AccountPage />}>
  <Route path="orders" element={<Orders />} />   {/* /account/orders */}
  <Route path="profile" element={<Profile />} /> {/* /account/profile */}
</Route>
```

The parent component decides **where** the child appears, with `<Outlet />`. `Outlet` is a placeholder. React Router puts the matching child component there.

## Index Routes

An **index route** is the child that shows when the address is exactly the parent's path. It has `index` instead of a `path`.

```tsx
<Route path="/account" element={<AccountPage />}>
  <Route index element={<AccountHome />} />      {/* /account */}
  <Route path="orders" element={<Orders />} />   {/* /account/orders */}
</Route>
```

Without an index route, the `Outlet` would be empty at `/account`.

## Route Layouts

A **layout route** is a parent route that holds the parts every page shares, such as the header, menu and footer. The pages are its children, shown in the `Outlet`.

<figure className="react-fig">
<svg viewBox="0 0 520 190" role="img" aria-labelledby="fig-r-layout" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-r-layout">A layout route keeps the header and footer. The Outlet is the hole where the child page appears</title>
  <rect x="110" y="8" width="300" height="174" rx="10" style={{ fill: "var(--panel)", stroke: "var(--accent)", strokeWidth: 1.5 }} />
  <text x="260" y="26" textAnchor="middle" style={{ fill: "var(--accent)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>Layout (stays the same)</text>
  <rect x="125" y="34" width="270" height="28" rx="8" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)", strokeWidth: 1.5 }} />
  <text x="260.0" y="53.0" textAnchor="middle" style={{ fill: "var(--accent)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Header · Home · Books · About</text>
  <rect x="125" y="70" width="270" height="70" rx="8" style={{ fill: "none", stroke: "var(--accent-secondary)", strokeWidth: 2, strokeDasharray: "6 4" }} />
  <text x="260" y="84" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 11, fontWeight: 700, fontFamily: "var(--mono)" }}>&lt;Outlet /&gt;</text>
  <g opacity="1">
    <rect x="150" y="90" width="220" height="40" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
    <text x="260.0" y="115.0" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>Home page</text>
    <animate attributeName="opacity" values="1;0;0;1" keyTimes="0.000;0.333;0.667;1" calcMode="discrete" dur="4.5s" repeatCount="indefinite" />
  </g>
  <g opacity="0">
    <rect x="150" y="90" width="220" height="40" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
    <text x="260.0" y="115.0" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>Books page</text>
    <animate attributeName="opacity" values="0;1;0;0" keyTimes="0.000;0.333;0.667;1" calcMode="discrete" dur="4.5s" repeatCount="indefinite" />
  </g>
  <g opacity="0">
    <rect x="150" y="90" width="220" height="40" rx="8" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
    <text x="260.0" y="115.0" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 13, fontWeight: 700, fontFamily: "var(--mono)" }}>About page</text>
    <animate attributeName="opacity" values="0;0;1;0" keyTimes="0.000;0.333;0.667;1" calcMode="discrete" dur="4.5s" repeatCount="indefinite" />
  </g>
  <rect x="125" y="148" width="270" height="26" rx="8" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="260.0" y="166.0" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Footer</text>
</svg>
<figcaption>The child page changes inside the Outlet. The header and footer are not drawn again.</figcaption>
</figure>

      <header>
        <strong>Book Corner</strong>
        <nav>
          <NavLink to="/" end>Home</NavLink>
          <NavLink to="/account">Account</NavLink>
          <NavLink to="/account/orders">Orders</NavLink>
        </nav>
      </header>

      <main>

      <footer>Open every day, 9 am to 9 pm</footer>
    </div>
  );
}
`,
    "App.tsx": `import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";

function Account() {
  return (
    <div>
      <h3>My account</h3>
  );
}

  return (
        <Route path="/" element={<Layout />}>
          <Route index element={<p>Home: new books every week.</p>} />
          <Route path="account" element={<Account />}>
            <Route index element={<p>Hello, Asha. Choose an option above.</p>} />
            <Route path="orders" element={<p>Order #1021 · Atomic Habits · delivered</p>} />
  );
}
`,
    "App.css": `.layout header { display: flex; gap: 14px; align-items: center; border-bottom: 1px solid #e2e8f0; padding-bottom: 6px; }
.layout nav { display: flex; gap: 8px; }
.layout nav a { color: #334155; }
.layout nav a.active { color: #0047ba; font-weight: 700; }
.layout main { min-height: 80px; }
.layout footer { border-top: 1px solid #e2e8f0; padding-top: 6px; color: #64748b; font-size: 13px; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click through the menu. The header and footer never change. Only the middle part changes:

- At `/`, the index route of the layout shows "Home".
- At `/account`, the index route of `Account` shows "Hello, Asha".
- At `/account/orders`, the `orders` route shows inside `Account`, which shows inside `Layout`.

Notice that child paths have **no** `/` at the start: `path="account"`, not `path="/account"`. A child path is added to its parent's path.

## Not Found Page

When the address matches no route, React shows nothing. To show a friendly message instead, add a route with `path="*"`. The `*` matches **any** address that no other route matched.

      <h2>Page not found</h2>
      <p>Sorry, we could not find that page.</p>
      <Link to="/">Go to the home page</Link>
    </div>
  );
}

  return (
      <nav>
        <Link to="/">Home</Link> | <Link to="/books">Books</Link> | <Link to="/old-offers-page">Old offers link</Link>
      </nav>
        <Route path="/" element={<h2>Home</h2>} />
        <Route path="/books" element={<h2>Books</h2>} />
        <Route path="*" element={<NotFound />} />
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click **Old offers link**. No route has that path, so the `*` route shows the "Page not found" message.

## Common mistakes

<div className="tscroll">

| Mistake | What you see | Fix |
|---|---|---|
| Using `Link` or `useNavigate` outside `BrowserRouter` | Error: `useHref() may be used only in the context of a <Router>` | Wrap the app in `<BrowserRouter>` |
| Using `<a href>` for pages inside the app | The page reloads and the state is lost | Use `<Link to>` |
| Forgetting `<Outlet />` in a parent route | The child page never shows | Add `<Outlet />` where the child should appear |
| Starting a child path with `/` | The child does not match as expected | Write `path="orders"`, not `path="/orders"` |
| Forgetting `end` on the Home `NavLink` | Home is always highlighted | Add `end` to the `/` link |
| Comparing a route parameter with a number | The item is not found | Parameters are text: compare with `"2"`, or use `Number(id)` |
| No `*` route | Unknown addresses show an empty page | Add `<Route path="*" element={<NotFound />} />` |

</div>

## Check Yourself

## Exercises

## What to Remember

- Wrap the app in `BrowserRouter`, list pages with `Routes` and `Route`, and move between them with `Link`, `NavLink` or `useNavigate`.
- Read `/books/:id` with `useParams`, and `?q=...` with `useSearchParams`.
- Use a layout route with `<Outlet />` for shared parts, `index` for the default child, and `path="*"` for a Not Found page.

Next: User Access Control
