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.