React Basics

Session 4 — Client-Side Routing with React Router

45 minutes
📚 Beginner
🎯 Routing & Navigation
Scroll to begin
Part 1 · Overview

Session Agenda

Real apps have multiple pages. Today we master React Router — the standard library for client-side navigation in React — from setup to advanced nested routes.

0 – 5 min

SPAs vs Traditional Apps

How the browser History API enables page-like navigation without reloads.

5 – 15 min

Setting Up React Router

Install, BrowserRouter, Routes, and Route — the three building blocks.

15 – 22 min

Link & NavLink

Client-side navigation components and auto-active styles for menus.

22 – 30 min

URL Parameters & Search Params

Dynamic routes with :id, useParams, and query strings with useSearchParams.

30 – 37 min

Programmatic Navigation

useNavigate for redirecting after actions like login or form submit.

37 – 42 min

Nested Routes & Layouts

Shared layouts, Outlet, and organizing complex route trees.

42 – 45 min

Recap & Quick Quiz

Review key concepts and test what you've learned.

Part 2 · How Routing Works

SPAs vs Traditional Apps

Before React Router, every navigation click sent a request to the server, which returned a completely new HTML page. React apps work differently — they are Single-Page Applications.

Traditional Multi-Page App
User clicks link HTTP request to server
Server returns full HTML Full page reload

All JavaScript state is lost. The browser repaints everything from scratch.

React SPA with React Router
User clicks Link History API updates URL
React swaps the component Instant, no reload

State is preserved. No server round trip. The navbar stays mounted between pages.

How It Works Under the Hood

React Router uses the browser's built-in History API (history.pushState) to change the URL without reloading the page. It then reads the current URL and renders the matching component.

📍

URL as State

The URL is just another piece of application state. When it changes, the right component re-renders.

No Server Needed

Navigation happens entirely in the browser. Only data requests go to the server.

🔗

Shareable URLs

Users can bookmark and share URLs like /users/42 — React Router recreates the right view.

🔙

Back & Forward Work

The browser history stack is real. The back and forward buttons navigate your app correctly.

Part 3 · Installation & Structure

Setting Up React Router

React Router is a separate package. Install it, then wrap your app in BrowserRouter and define your routes with Routes and Route.

Terminal — Install
npm install react-router-dom
main.jsx — Wrap the App
import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App'

ReactDOM.createRoot(document.getElementById('root')).render(
  <BrowserRouter>   {/* ← wrap once here, at the top */}
    <App />
  </BrowserRouter>
)
App.jsx — Define Routes
import { Routes, Route } from 'react-router-dom'
import HomePage   from './pages/HomePage'
import AboutPage  from './pages/AboutPage'
import UserPage   from './pages/UserPage'
import NotFound   from './pages/NotFound'

function App() {
  return (
    <Routes>
      <Route path="/"          element={<HomePage />} />
      <Route path="/about"     element={<AboutPage />} />
      <Route path="/users/:id" element={<UserPage />} />
      <Route path="*"          element={<NotFound />} />
    </Routes>
  )
}
1

BrowserRouter — one time, at the root

Provides routing context to your entire app. Goes in main.jsx, wrapping <App>. Never nest a second BrowserRouter inside.

2

Routes — the switch block

Looks at the current URL and renders only the first matching Route. No need for the old "exact" keyword — v6 matches exactly by default.

3

Route — one per page

Maps a URL pattern to a component. path="/users/:id" captures dynamic segments; path="*" is the catch-all 404 route.

path pattern Matches Does NOT match
"/" / /about, /users
"/about" /about /about/team
"/users/:id" /users/1, /users/42 /users, /users/1/posts
"*" anything unmatched
Part 5 · Dynamic Routes

URL Parameters

Real apps have dynamic URLs — /users/42, /posts/react-basics. Define the dynamic segment with a colon :param, then read it with the useParams hook.

useParams — Reading Dynamic Segments
import { useParams } from 'react-router-dom'

// Route definition: <Route path="/users/:id" element={<UserPage />} />

function UserPage() {
  const { id } = useParams()
  // URL /users/42 → id = "42" (always a string)
  // URL /users/ahmed → id = "ahmed"

  return <h2>Viewing user {id}</h2>
}

// Multiple params: /posts/:category/:slug
function PostPage() {
  const { category, slug } = useParams()
  // /posts/react/hooks-tutorial → category="react", slug="hooks-tutorial"
  return <p>{category} / {slug}</p>
}
💡 Params are Always Strings

Even if the URL has a number like /users/42, useParams() returns it as the string "42". Parse it if you need a number: const numId = Number(id).

Query Strings with useSearchParams

Query strings are the ?key=value part of a URL — /search?q=react&page=2. Use useSearchParams to read and update them.

useSearchParams — Query Strings
import { useSearchParams } from 'react-router-dom'

function SearchPage() {
  const [searchParams, setSearchParams] = useSearchParams()

  // Read: /search?q=react&page=2
  const query = searchParams.get('q')    // "react"
  const page  = searchParams.get('page') // "2"

  function goToPage3() {
    // Write: updates URL to /search?q=react&page=3
    setSearchParams({ q: query, page: '3' })
  }

  return (
    <div>
      <p>Searching for: {query} (page {page})</p>
      <button onClick={goToPage3}>Next Page</button>
    </div>
  )
}
Hook Reads from Example URL Example value
useParams() URL path segments /users/42 id = "42"
useSearchParams() Query string /search?q=react get('q') = "react"
Part 7 · Layouts & Nesting

Nested Routes & Outlet

Nested routes let child routes render inside a parent layout — perfect for dashboards, settings pages, or any UI where a section of the page stays fixed while a sub-section changes.

Nested Route Layout
DashboardLayout — always visible (navbar, sidebar)
<Outlet />
renders child here
/dashboard → Overview
/dashboard/settings → Settings
/dashboard/profile → Profile
App.jsx — Nested Route Definition
import { Routes, Route } from 'react-router-dom'

function App() {
  return (
    <Routes>
      <Route path="/" element={<HomePage />} />

      {/* Parent route — renders DashboardLayout */}
      <Route path="/dashboard" element={<DashboardLayout />}>

        {/* index — matches /dashboard exactly */}
        <Route index element={<Overview />} />

        {/* child routes — match /dashboard/settings, /dashboard/profile */}
        <Route path="settings" element={<Settings />} />
        <Route path="profile"  element={<Profile />} />
      </Route>

      <Route path="*" element={<NotFound />} />
    </Routes>
  )
}
DashboardLayout.jsx — The Outlet
import { Outlet, NavLink } from 'react-router-dom'

function DashboardLayout() {
  return (
    <div className="dashboard">

      <aside>  {/* Sidebar stays mounted for all child routes */}
        <NavLink to="/dashboard" end>Overview</NavLink>
        <NavLink to="/dashboard/settings">Settings</NavLink>
        <NavLink to="/dashboard/profile">Profile</NavLink>
      </aside>

      <main>
        <Outlet />  {/* child page renders HERE */}
      </main>

    </div>
  )
}
🧠 The end Prop on NavLink

Without end, the /dashboard link would stay "active" on /dashboard/settings too (because /dashboard is a prefix). The end prop tells NavLink to only match the exact path.

1

index route — the default child

<Route index /> renders when the parent path matches exactly. For /dashboard it shows Overview; navigating to /dashboard/settings replaces it with Settings.

2

Child paths are relative

Inside a nested route, you write path="settings", not path="/dashboard/settings". React Router prepends the parent path automatically.

3

Outlet is the injection point

The parent layout component must render <Outlet /> wherever it wants child pages to appear. Without it, child routes will match but show nothing.

Part 8 · Wrap-Up

Recap & Quick Quiz

Let's review the key concepts from today's session.

BrowserRouter + Routes + Route

The three building blocks. BrowserRouter wraps the app once; Routes + Route map URLs to components.

Link & NavLink

Use Link for internal navigation (never <a href>). NavLink auto-applies the active class for menus.

useParams & useSearchParams

Read dynamic path segments (:id) with useParams. Read query strings (?q=...) with useSearchParams.

useNavigate & Nested Routes

Redirect programmatically with useNavigate. Share layouts across child routes with Outlet.

🧪 Quiz — Question 1 of 3

Why should you use <Link to="/"> instead of <a href="/"> inside a React app?

🧪 Quiz — Question 2 of 3

Given the route path="/products/:id" and the URL /products/99, what does useParams() return?

🧪 Quiz — Question 3 of 3

In a nested route, what renders the child page inside the parent layout?
🎯 What's Next?

In Session 5, we master forms & validation — controlled inputs for every input type, multi-field forms, submit handling, validation patterns, and real-time user feedback.