Routing with React Router

Implement client-side routing in your React applications.

Understanding React Router: Building Single Page Applications with Navigation

React Router is the standard routing library for React applications. It enables you to build single-page applications with multiple views, allowing users to navigate between different components while maintaining the application state. Think of it as the GPS system for your React app - it helps users navigate to different destinations (components) within your application.

What is Client-Side Routing? Traditional web applications reload the entire page when navigating between different URLs. This creates a jarring experience with page flashes and lost application state. Client-side routing, which React Router provides, updates the URL and renders different components without reloading the page, creating a smooth, app-like experience.

Why React Router?

  • Declarative routing: Define routes using JSX components
  • Dynamic route matching: Handle parameters and query strings
  • Nested routing: Build complex layouts with nested route structures
  • History management: Navigate programmatically and handle browser back/forward
  • Code splitting: Load components only when needed

Core Components of React Router

  1. BrowserRouter: The router that uses HTML5 history API
  2. Routes: Container for all your route definitions
  3. Route: Defines a mapping between a URL path and a component
  4. Link: Creates navigation links (replaces anchor tags)
  5. useNavigate: Hook for programmatic navigation

Installation React Router is not included with React by default. Install it using:

npm install react-router-dom

Real-World Analogy Think of React Router like the floor plan of an office building:

  • BrowserRouter is the building itself
  • Routes are the hallways connecting different areas
  • Route components are the doors to specific rooms
  • Link components are the signs pointing to different rooms
  • useNavigate is like having a building guide who can take you anywhere
1// 🏢 REACT ROUTER BASICS: Building a Multi-Page SPA
2
3import React from 'react';
4import {
5 BrowserRouter,
6 Routes,
7 Route,
8 Link,
9 useNavigate,
10 useLocation,
11 NavLink
12} from 'react-router-dom';
13
14// Basic page components
15function HomePage() {
16 return (
17 <div>
18 <h1>Home Page</h1>
19 <p>Welcome to our React Router demo!</p>
20 </div>
21 );
22}
23
24function AboutPage() {
25 const navigate = useNavigate();
26
27 return (
28 <div>
29 <h1>About Us</h1>
30 <button onClick={() => navigate('/contact')}>
31 Go to Contact
32 </button>
33 </div>
34 );
35}
36
37function ContactPage() {
38 const location = useLocation();
39
40 return (
41 <div>
42 <h1>Contact</h1>
43 <p>Current path: {location.pathname}</p>
44 </div>
45 );
46}
47
48// Navigation component with NavLink
49function Navigation() {
50 return (
51 <nav>
52 <NavLink
53 to="/"
54 style={({ isActive }) => ({
55 color: isActive ? 'red' : 'blue',
56 marginRight: '10px'
57 })}
58 >
59 Home
60 </NavLink>
61 <NavLink
62 to="/about"
63 style={({ isActive }) => ({
64 color: isActive ? 'red' : 'blue',
65 marginRight: '10px'
66 })}
67 >
68 About
69 </NavLink>
70 <NavLink
71 to="/contact"
72 style={({ isActive }) => ({
73 color: isActive ? 'red' : 'blue'
74 })}
75 >
76 Contact
77 </NavLink>
78 </nav>
79 );
80}
81
82// Main App with routing
83function App() {
84 return (
85 <BrowserRouter>
86 <Navigation />
87
88 <Routes>
89 <Route path="/" element={<HomePage />} />
90 <Route path="/about" element={<AboutPage />} />
91 <Route path="/contact" element={<ContactPage />} />
92 <Route path="*" element={<h1>404 - Page Not Found</h1>} />
93 </Routes>
94 </BrowserRouter>
95 );
96}
97
98// Programmatic navigation example
99function NavigationControls() {
100 const navigate = useNavigate();
101 const location = useLocation();
102
103 return (
104 <div>
105 <p>Current: {location.pathname}</p>
106 <button onClick={() => navigate(-1)}>Back</button>
107 <button onClick={() => navigate('/')}>Home</button>
108 <button onClick={() => navigate('/about', { replace: true })}>
109 Replace with About
110 </button>
111 </div>
112 );
113}
114
115export default App;

Dynamic Routes with Parameters: Creating Flexible URLs

Dynamic routes allow you to create flexible URLs that can handle variable content. Instead of creating a separate route for every user, product, or blog post, you can create one dynamic route that handles all of them. This is essential for building scalable applications.

Understanding Route Parameters Route parameters are placeholders in your route paths that get filled with actual values when users navigate. They're denoted with a colon (:) followed by the parameter name.

For example:

  • /users/:id matches /users/123, /users/abc, etc.
  • /products/:category/:id matches /products/electronics/42

Types of Dynamic Routes

  1. Single parameter: /users/:id
  2. Multiple parameters: /posts/:year/:month/:slug
  3. Optional parameters: /products/:category/:id?
  4. Wildcard/catch-all: /docs/*

The useParams Hook React Router provides the useParams hook to access route parameters in your components. It returns an object with key-value pairs of the URL parameters.

Real-World Analogy Think of route parameters like a hotel keycard system:

  • The route pattern (/room/:number) is like the card reader
  • The actual URL (/room/405) is like a specific keycard
  • useParams extracts "405" so you know which room to display

Common Use Cases

  • User profiles: /users/:username
  • Product details: /products/:productId
  • Blog posts: /blog/:year/:month/:slug
  • Category pages: /shop/:category
  • Search results: /search/:query
1// 🎯 DYNAMIC ROUTING EXAMPLES
2
3import React, { useState, useEffect } from 'react';
4import {
5 BrowserRouter,
6 Routes,
7 Route,
8 Link,
9 useParams,
10 useNavigate,
11 useSearchParams
12} from 'react-router-dom';
13
14// Single parameter example
15function UserProfile() {
16 const { userId } = useParams();
17 const users = {
18 '1': { name: 'Alice', role: 'Developer' },
19 '2': { name: 'Bob', role: 'Designer' }
20 };
21
22 const user = users[userId];
23 if (!user) return <div>User not found!</div>;
24
25 return (
26 <div>
27 <h1>{user.name}</h1>
28 <p>Role: {user.role}</p>
29 <Link to="/users/1">Alice</Link>
30 <Link to="/users/2">Bob</Link>
31 </div>
32 );
33}
34
35// Multiple parameters example
36function ProductDetails() {
37 const { category, productId } = useParams();
38 const navigate = useNavigate();
39
40 return (
41 <div>
42 <h1>Product {productId} in {category}</h1>
43 <button onClick={() => navigate(-1)}>Back</button>
44 </div>
45 );
46}
47
48// Query parameters example
49function SearchPage() {
50 const [searchParams, setSearchParams] = useSearchParams();
51 const query = searchParams.get('q') || '';
52
53 const handleSearch = (e) => {
54 e.preventDefault();
55 const formData = new FormData(e.target);
56 setSearchParams({ q: formData.get('query') });
57 };
58
59 return (
60 <div>
61 <form onSubmit={handleSearch}>
62 <input name="query" defaultValue={query} placeholder="Search..." />
63 <button type="submit">Search</button>
64 </form>
65 {query && <p>Results for "{query}"</p>}
66 </div>
67 );
68}
69
70// Wildcard routes example
71function Documentation() {
72 const params = useParams();
73 const path = params['*'] || 'index';
74
75 return (
76 <div>
77 <h1>Docs: {path}</h1>
78 <nav>
79 <Link to="/docs">Home</Link>
80 <Link to="/docs/getting-started">Getting Started</Link>
81 <Link to="/docs/api/users">API Users</Link>
82 </nav>
83 </div>
84 );
85}
86
87// Main app with dynamic routes
88function App() {
89 return (
90 <BrowserRouter>
91 <Routes>
92 <Route path="/" element={<h1>Dynamic Routing</h1>} />
93 <Route path="/users/:userId" element={<UserProfile />} />
94 <Route path="/products/:category/:productId" element={<ProductDetails />} />
95 <Route path="/search" element={<SearchPage />} />
96 <Route path="/docs/*" element={<Documentation />} />
97 </Routes>
98 </BrowserRouter>
99 );
100}
101
102export default App;

Nested Routes: Building Hierarchical Application Layouts

Nested routes are a powerful feature that allows you to build complex, hierarchical layouts where child routes render inside parent routes. This mirrors the component composition pattern that makes React so powerful, but applied to routing.

Understanding Nested Routes Nested routes create a parent-child relationship between routes. When a nested route is matched, both the parent and child components render, with the child component appearing where the parent specifies.

The Outlet Component The Outlet component is where child routes render within their parent. Think of it as a placeholder that gets filled with the matching child route's component. It's similar to props.children but specifically for routes.

Benefits of Nested Routes

  1. Shared layouts: Common UI elements (headers, sidebars) stay consistent
  2. Hierarchical organization: URLs mirror your UI structure
  3. Code reusability: Parent components handle shared logic
  4. Better UX: Partial page updates instead of full refreshes

Real-World Analogy Think of nested routes like Russian nesting dolls (Matryoshka):

  • The outer doll (parent route) contains the shell
  • Inner dolls (child routes) fit inside at designated spots
  • Each level adds its own content while maintaining the outer structure

Common Patterns

  • Dashboard layouts with sidebar navigation
  • Settings pages with subsections
  • Admin panels with multiple management areas
  • E-commerce category and product hierarchies
  • Multi-step forms with progress indicators
1// 🏗️ NESTED ROUTES EXAMPLE
2
3import React from 'react';
4import {
5 BrowserRouter,
6 Routes,
7 Route,
8 Link,
9 Outlet,
10 NavLink
11} from 'react-router-dom';
12
13// Layout component with sidebar
14function DashboardLayout() {
15 return (
16 <div style={{ display: 'flex' }}>
17 <aside style={{ width: '200px', backgroundColor: '#f0f0f0', padding: '20px' }}>
18 <h2>Dashboard</h2>
19 <nav>
20 <NavLink to="/dashboard" end style={{ display: 'block', margin: '10px 0' }}>
21 Overview
22 </NavLink>
23 <NavLink to="/dashboard/users" style={{ display: 'block', margin: '10px 0' }}>
24 Users
25 </NavLink>
26 <NavLink to="/dashboard/settings" style={{ display: 'block', margin: '10px 0' }}>
27 Settings
28 </NavLink>
29 </nav>
30 </aside>
31
32 <main style={{ flex: 1, padding: '20px' }}>
33 {/* Child routes render here */}
34 <Outlet />
35 </main>
36 </div>
37 );
38}
39
40// Dashboard pages
41function Overview() {
42 return <h1>Dashboard Overview</h1>;
43}
44
45function Users() {
46 return (
47 <div>
48 <h1>Users</h1>
49 <nav>
50 <Link to="/dashboard/users">All</Link>
51 <Link to="/dashboard/users/active">Active</Link>
52 </nav>
53 <Outlet />
54 </div>
55 );
56}
57
58function AllUsers() {
59 return <p>Showing all users...</p>;
60}
61
62function ActiveUsers() {
63 return <p>Showing active users...</p>;
64}
65
66function Settings() {
67 return (
68 <div>
69 <h1>Settings</h1>
70 <nav>
71 <NavLink to="/dashboard/settings/profile">Profile</NavLink>
72 <NavLink to="/dashboard/settings/security">Security</NavLink>
73 </nav>
74 <Outlet />
75 </div>
76 );
77}
78
79function ProfileSettings() {
80 return <h2>Profile Settings</h2>;
81}
82
83function SecuritySettings() {
84 return <h2>Security Settings</h2>;
85}
86
87// App with nested route configuration
88function App() {
89 return (
90 <BrowserRouter>
91 <Routes>
92 <Route path="/" element={<h1>Home</h1>} />
93
94 {/* Dashboard routes with nested structure */}
95 <Route path="/dashboard" element={<DashboardLayout />}>
96 <Route index element={<Overview />} />
97
98 <Route path="users" element={<Users />}>
99 <Route index element={<AllUsers />} />
100 <Route path="active" element={<ActiveUsers />} />
101 </Route>
102
103 <Route path="settings" element={<Settings />}>
104 <Route path="profile" element={<ProfileSettings />} />
105 <Route path="security" element={<SecuritySettings />} />
106 </Route>
107 </Route>
108 </Routes>
109 </BrowserRouter>
110 );
111}
112
113export default App;

Protected Routes: Securing Your Application with Authentication and Authorization

Protected routes are essential for building secure applications. They prevent unauthorized users from accessing certain parts of your application by checking authentication status and user permissions before rendering components.

Authentication vs Authorization

  • Authentication: Verifying who the user is (login status)
  • Authorization: Verifying what the user can do (permissions/roles)

Common Protection Patterns

  1. Login Protection: Redirect to login if not authenticated
  2. Role-Based Access: Different UI for different user roles
  3. Permission-Based: Fine-grained control based on specific permissions
  4. Subscription-Based: Premium features for paying users
  5. Conditional Rendering: Show/hide UI elements based on auth state

Implementation Strategies

  • Higher-Order Components (HOC): Wrap components with auth logic
  • Custom Route Components: Create ProtectedRoute components
  • Context + Hooks: Centralized auth state management
  • Route Guards: Check permissions before navigation

Security Best Practices

  • Never trust client-side protection alone
  • Always validate permissions on the server
  • Store sensitive data securely
  • Implement proper session management
  • Handle edge cases (expired tokens, network errors)

User Experience Considerations

  • Show loading states during auth checks
  • Provide clear feedback for unauthorized access
  • Remember intended destination after login
  • Implement smooth redirects
1// 🔐 PROTECTED ROUTES EXAMPLE
2
3import React, { createContext, useContext, useState } from 'react';
4import {
5 BrowserRouter,
6 Routes,
7 Route,
8 Navigate,
9 useNavigate,
10 useLocation,
11 Link
12} from 'react-router-dom';
13
14// Authentication Context
15const AuthContext = createContext(null);
16
17function AuthProvider({ children }) {
18 const [user, setUser] = useState(null);
19
20 const login = (username, password) => {
21 // Mock authentication
22 if (username === 'admin' && password === 'admin') {
23 setUser({ username, role: 'admin' });
24 return true;
25 } else if (username === 'user' && password === 'user') {
26 setUser({ username, role: 'user' });
27 return true;
28 }
29 return false;
30 };
31
32 const logout = () => {
33 setUser(null);
34 };
35
36 return (
37 <AuthContext.Provider value={{ user, login, logout }}>
38 {children}
39 </AuthContext.Provider>
40 );
41}
42
43function useAuth() {
44 return useContext(AuthContext);
45}
46
47// Protected Route Component
48function ProtectedRoute({ children }) {
49 const { user } = useAuth();
50 const location = useLocation();
51
52 if (!user) {
53 // Redirect to login and save the attempted location
54 return <Navigate to="/login" state={{ from: location }} replace />;
55 }
56
57 return children;
58}
59
60// Role-Based Protected Route
61function AdminRoute({ children }) {
62 const { user } = useAuth();
63
64 if (!user) {
65 return <Navigate to="/login" replace />;
66 }
67
68 if (user.role !== 'admin') {
69 return <Navigate to="/unauthorized" replace />;
70 }
71
72 return children;
73}
74
75// Public Pages
76function HomePage() {
77 const { user } = useAuth();
78
79 return (
80 <div>
81 <h1>Home</h1>
82 {user ? (
83 <p>Welcome, {user.username}!</p>
84 ) : (
85 <p>Please login to access protected features.</p>
86 )}
87 </div>
88 );
89}
90
91function LoginPage() {
92 const [username, setUsername] = useState('');
93 const [password, setPassword] = useState('');
94 const { login } = useAuth();
95 const navigate = useNavigate();
96 const location = useLocation();
97
98 const from = location.state?.from?.pathname || '/dashboard';
99
100 const handleSubmit = (e) => {
101 e.preventDefault();
102 if (login(username, password)) {
103 navigate(from, { replace: true });
104 } else {
105 alert('Invalid credentials');
106 }
107 };
108
109 return (
110 <div>
111 <h1>Login</h1>
112 <p>Demo: admin/admin or user/user</p>
113 <form onSubmit={handleSubmit}>
114 <input
115 type="text"
116 placeholder="Username"
117 value={username}
118 onChange={(e) => setUsername(e.target.value)}
119 />
120 <input
121 type="password"
122 placeholder="Password"
123 value={password}
124 onChange={(e) => setPassword(e.target.value)}
125 />
126 <button type="submit">Login</button>
127 </form>
128 </div>
129 );
130}
131
132// Protected Pages
133function Dashboard() {
134 const { user } = useAuth();
135 return (
136 <div>
137 <h1>Dashboard</h1>
138 <p>Welcome to your dashboard, {user.username}!</p>
139 </div>
140 );
141}
142
143function AdminPanel() {
144 return (
145 <div>
146 <h1>Admin Panel</h1>
147 <p>Only admins can see this page.</p>
148 </div>
149 );
150}
151
152// Navigation
153function Navigation() {
154 const { user, logout } = useAuth();
155 const navigate = useNavigate();
156
157 const handleLogout = () => {
158 logout();
159 navigate('/');
160 };
161
162 return (
163 <nav>
164 <Link to="/">Home</Link>
165 {user ? (
166 <>
167 <Link to="/dashboard">Dashboard</Link>
168 {user.role === 'admin' && <Link to="/admin">Admin</Link>}
169 <button onClick={handleLogout}>Logout</button>
170 </>
171 ) : (
172 <Link to="/login">Login</Link>
173 )}
174 </nav>
175 );
176}
177
178// Main App
179function App() {
180 return (
181 <AuthProvider>
182 <BrowserRouter>
183 <Navigation />
184
185 <Routes>
186 {/* Public routes */}
187 <Route path="/" element={<HomePage />} />
188 <Route path="/login" element={<LoginPage />} />
189
190 {/* Protected routes */}
191 <Route
192 path="/dashboard"
193 element={
194 <ProtectedRoute>
195 <Dashboard />
196 </ProtectedRoute>
197 }
198 />
199
200 {/* Admin only routes */}
201 <Route
202 path="/admin"
203 element={
204 <AdminRoute>
205 <AdminPanel />
206 </AdminRoute>
207 }
208 />
209
210 <Route path="/unauthorized" element={<h1>Unauthorized</h1>} />
211 </Routes>
212 </BrowserRouter>
213 </AuthProvider>
214 );
215}
216
217export default App;