JavaScript and React course Β· Module 6: React Router
Complete Routing Architecture - Navigation Command Center
In this lesson9
Navigator, we've reached the final stage of our journey through the Asteroid Belt! It's time to gather all the navigation techniques we've learned and build a complete routing system - a true Navigation Command Center for our cosmic fleet.
In this lesson, we'll see how all React Router elements work together in one cohesive application.
Architecture of a Complete Application with Routing
When building an application with full routing, we need to plan several layers:
- Authentication layer - AuthContext + ProtectedRoute
- Navigation layer - NavLink with active styles
- Routes layer - nested Routes with Outlet
- Parameters layer - dynamic paths with useParams
- Redirects layer - 404 handling and security
Folder Structure
In larger applications, it's worth organizing routing-related files in a logical structure:
1src/
2βββ App.jsx // Main component with BrowserRouter
3βββ routes/
4β βββ AppRoutes.jsx // Definition of all routes
5β βββ ProtectedRoute.jsx
6βββ context/
7β βββ AuthContext.jsx // Authentication context
8βββ layouts/
9β βββ MainLayout.jsx // Layout with navigation and Outlet
10β βββ DashboardLayout.jsx
11βββ pages/
12β βββ HomePage.jsx
13β βββ LoginPage.jsx
14β βββ MissionDetail.jsx
15β βββ NotFound.jsx
16βββ components/
17 βββ Navigation.jsx // NavLink menuSuch organization allows easy management of even extensive applications with dozens of routes.
Connecting AuthContext with Routing
The central element is the AuthProvider wrapping the entire application:
1function AuthProvider({ children }) {
2 const [user, setUser] = useState(null);
3
4 const login = (name) => setUser({ name });
5 const logout = () => setUser(null);
6
7 return (
8 <AuthContext.Provider value={{ user, login, logout }}>
9 {children}
10 </AuthContext.Provider>
11 );
12}
13
14// In App.jsx:
15function App() {
16 return (
17 <AuthProvider>
18 <BrowserRouter>
19 <Navigation />
20 <Routes>
21 {/* public and protected routes */}
22 </Routes>
23 </BrowserRouter>
24 </AuthProvider>
25 );
26}The AuthProvider must be outside BrowserRouter so that the authentication context is available in all route components.
Navigation with NavLink and Conditional Rendering
The navigation component combines NavLink (with active styles) with AuthContext (conditional display of options):
1function Navigation() {
2 const { user, logout } = useAuth();
3
4 return (
5 <nav>
6 <NavLink to="/" className={({isActive}) => isActive ? 'active' : ''}>
7 Home
8 </NavLink>
9 <NavLink to="/missions" className={({isActive}) => isActive ? 'active' : ''}>
10 Missions
11 </NavLink>
12
13 {user ? (
14 <>
15 <NavLink to="/dashboard" className={({isActive}) => isActive ? 'active' : ''}>
16 Dashboard
17 </NavLink>
18 <span>Logged in: {user.name}</span>
19 <button onClick={logout}>Logout</button>
20 </>
21 ) : (
22 <NavLink to="/login">Login</NavLink>
23 )}
24 </nav>
25 );
26}This way the navigation automatically responds to the authentication state - a logged-in user sees the Dashboard and logout button, while an unauthenticated user sees the login link.
Nested Routes with Layout
The missions section uses nested routes with Outlet:
1function MissionsLayout() {
2 return (
3 <div className="missions-layout">
4 <aside>
5 <h3>Missions</h3>
6 <NavLink to="/missions/apollo">Apollo</NavLink>
7 <NavLink to="/missions/artemis">Artemis</NavLink>
8 <NavLink to="/missions/voyager">Voyager</NavLink>
9 </aside>
10 <main>
11 <Outlet />
12 </main>
13 </div>
14 );
15}
16
17// In route configuration:
18<Route path="/missions" element={<MissionsLayout />}>
19 <Route index element={<MissionsList />} />
20 <Route path=":missionId" element={<MissionDetail />} />
21</Route>The index route (<Route index />) renders the default content (missions list) when the user is at /missions. The dynamic parameter :missionId handles specific mission details.
Complete Route Configuration
Here's what the full route configuration looks like combining all the techniques we've learned:
1function App() {
2 return (
3 <AuthProvider>
4 <BrowserRouter>
5 <Navigation />
6 <Routes>
7 {/* Home page */}
8 <Route path="/" element={<HomePage />} />
9
10 {/* Login */}
11 <Route path="/login" element={<LoginPage />} />
12
13 {/* Nested routes with layout */}
14 <Route path="/missions" element={<MissionsLayout />}>
15 <Route index element={<MissionsList />} />
16 <Route path=":missionId" element={<MissionDetail />} />
17 </Route>
18
19 {/* Protected route */}
20 <Route path="/dashboard" element={
21 <ProtectedRoute>
22 <DashboardPage />
23 </ProtectedRoute>
24 } />
25
26 {/* 404 handling */}
27 <Route path="*" element={<NotFound />} />
28 </Routes>
29 </BrowserRouter>
30 </AuthProvider>
31 );
32}The order of elements matters:
- Specific paths (
/,/login,/missions) are matched first - The
path="*"route must be last - it catches everything that doesn't match any defined route
Login with Path Remembering
The login page uses useNavigate and useLocation to redirect the user back to the page they were redirected from:
1function LoginPage() {
2 const [name, setName] = useState('');
3 const { login } = useAuth();
4 const navigate = useNavigate();
5 const location = useLocation();
6
7 // Path the user was redirected from
8 const from = location.state?.from?.pathname || '/dashboard';
9
10 const handleSubmit = (e) => {
11 e.preventDefault();
12 if (name.trim()) {
13 login(name);
14 navigate(from, { replace: true }); // replace: true removes /login from history
15 }
16 };
17
18 return (
19 <form onSubmit={handleSubmit}>
20 <input value={name} onChange={(e) => setName(e.target.value)} />
21 <button type="submit">Log In</button>
22 </form>
23 );
24}The replace: true option in navigate is crucial - thanks to it, the user won't return to the login page after clicking "Back" in the browser.
404 Page Handling
The Not Found page contains a link back to the home page:
1function NotFound() {
2 return (
3 <div className="not-found">
4 <h1>404</h1>
5 <p>Page not found</p>
6 <Link to="/">Return to home page</Link>
7 </div>
8 );
9}Architecture Summary
A complete application with routing in React combines the following elements:
| Element | Role | Hook / Component |
|---|---|---|
| Route container | Enables routing | BrowserRouter |
| Route definition | Maps URLs to components | Routes, Route |
| Navigation | Links with active styles | NavLink |
| Nesting | Shared layout | Outlet, Route children |
| Dynamic parameters | Element details | useParams |
| Programmatic navigation | Redirect after action | useNavigate |
| Location state | Passing hidden data | useLocation |
| Route protection | Enforcing login | ProtectedRoute + Navigate |
| Error handling | Non-existent pages | Route path="*" |
| Auth context | Global user data | createContext + useContext |
Try the complete application in the editor below - log in, navigate between missions, check the Dashboard, and test the 404 page!
Code for this lesson: App.jsx
1import React, { useState, createContext, useContext, Suspense, lazy } from 'react';
2import {
3 BrowserRouter, Routes, Route, Link, NavLink,
4 useParams, useNavigate, useLocation, Navigate, Outlet
5} from 'react-router-dom';
6
7// Auth Context
8const AuthContext = createContext();
9function useAuth() { return useContext(AuthContext); }
10
11function AuthProvider({ children }) {
12 const [user, setUser] = useState(null);
13 const login = (name) => setUser({ name });
14 const logout = () => setUser(null);
15 return (
16 <AuthContext.Provider value={{ user, login, logout }}>
17 {children}
18 </AuthContext.Provider>
19 );
20}
21
22// Protected Route
23function ProtectedRoute({ children }) {
24 const { user } = useAuth();
25 const location = useLocation();
26 if (!user) return <Navigate to="/login" state={{ from: location }} replace />;
27 return children;
28}
29
30// Components
31function Navigation() {
32 const { user, logout } = useAuth();
33 return (
34 <nav>
35 <div className="nav-brand">SpaceApp</div>
36 <div className="nav-links">
37 <NavLink to="/" className={({isActive}) => isActive ? 'active' : ''}>Home</NavLink>
38 <NavLink to="/missions" className={({isActive}) => isActive ? 'active' : ''}>Missions</NavLink>
39 {user ? (
40 <>
41 <NavLink to="/dashboard" className={({isActive}) => isActive ? 'active' : ''}>Dashboard</NavLink>
42 <span className="user-badge">{user.name}</span>
43 <button onClick={logout}>Logout</button>
44 </>
45 ) : (
46 <NavLink to="/login" className={({isActive}) => isActive ? 'active' : ''}>Login</NavLink>
47 )}
48 </div>
49 </nav>
50 );
51}
52
53function HomePage() {
54 return (
55 <div className="page home">
56 <h1>Welcome to SpaceApp</h1>
57 <p>Your gateway to space exploration</p>
58 <div className="features">
59 <div className="feature">Track Missions</div>
60 <div className="feature">View Analytics</div>
61 <div className="feature">Manage Crew</div>
62 </div>
63 </div>
64 );
65}
66
67function LoginPage() {
68 const [name, setName] = useState('');
69 const { login } = useAuth();
70 const navigate = useNavigate();
71 const location = useLocation();
72 const from = location.state?.from?.pathname || '/dashboard';
73
74 const handleSubmit = (e) => {
75 e.preventDefault();
76 if (name.trim()) {
77 login(name);
78 navigate(from, { replace: true });
79 }
80 };
81
82 return (
83 <div className="page login">
84 <h1>Login</h1>
85 <form onSubmit={handleSubmit}>
86 <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Username" />
87 <button type="submit">Login</button>
88 </form>
89 </div>
90 );
91}
92
93function MissionsLayout() {
94 return (
95 <div className="missions-layout">
96 <aside>
97 <h3>Missions</h3>
98 <NavLink to="/missions/apollo">Apollo</NavLink>
99 <NavLink to="/missions/artemis">Artemis</NavLink>
100 <NavLink to="/missions/voyager">Voyager</NavLink>
101 </aside>
102 <main><Outlet /></main>
103 </div>
104 );
105}
106
107function MissionsList() {
108 return <div className="content"><h2>Select a mission</h2></div>;
109}
110
111function MissionDetail() {
112 const { missionId } = useParams();
113 return (
114 <div className="content">
115 <h2>Mission: {missionId?.toUpperCase()}</h2>
116 <p>Details for {missionId} mission</p>
117 </div>
118 );
119}
120
121function DashboardPage() {
122 const { user } = useAuth();
123 return (
124 <div className="page dashboard">
125 <h1>Dashboard</h1>
126 <p>Welcome back, {user?.name}!</p>
127 <div className="stats">
128 <div className="stat"><span>Missions</span>12</div>
129 <div className="stat"><span>Crew</span>48</div>
130 <div className="stat"><span>Ships</span>8</div>
131 </div>
132 </div>
133 );
134}
135
136function NotFound() {
137 return (
138 <div className="page not-found">
139 <h1>404</h1>
140 <p>Page not found</p>
141 <Link to="/">Go Home</Link>
142 </div>
143 );
144}
145
146function App() {
147 return (
148 <AuthProvider>
149 <BrowserRouter>
150 <div className="app">
151 <Navigation />
152 <Routes>
153 <Route path="/" element={<HomePage />} />
154 <Route path="/login" element={<LoginPage />} />
155 <Route path="/missions" element={<MissionsLayout />}>
156 <Route index element={<MissionsList />} />
157 <Route path=":missionId" element={<MissionDetail />} />
158 </Route>
159 <Route path="/dashboard" element={
160 <ProtectedRoute><DashboardPage /></ProtectedRoute>
161 } />
162 <Route path="*" element={<NotFound />} />
163 </Routes>
164 </div>
165 </BrowserRouter>
166 </AuthProvider>
167 );
168}
169
170export default App;Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. How do you implement a Protected Route in React Router?
2. Where in the parent component do you place <Outlet /> to render nested routes?
These are 2 of 7 questions for this lesson. Solve the rest in the game.
Hands-on tasks in the game
- Code editor
Implement 404 error handling with Route path='*' and a NotFound component. Add navigation with links to existing and non-existent pages to test the error handling.
- Click in order
Arrange the programmatic navigation with state:
- Vertical ordering
Arrange the steps for programmatic navigation using useNavigate:
- Vertical ordering
Arrange the steps for passing and reading data between routes:
- Vertical ordering
Arrange the steps for implementing a Protected Route with the Navigate component:
- Click in order
Arrange the search params management:
- Vertical ordering
Arrange the navigation steps in React Router from clicking a link to displaying the page:
- Vertical ordering
Arrange the steps for handling unauthorized access to a protected route:
- Horizontal ordering
Arrange the elements of a route handling the 404 page:
- Horizontal ordering
Arrange the Route syntax with a dynamic id parameter:
- Code editor
Build a comprehensive Navigation System including: React Router setup with BrowserRouter, nested routes for the /fleet/:id/missions/:missionId structure, dynamic routing with params, Link and NavLink components with active styles, useNavigate for programmatic navigation, useParams and useLocation hooks, protected routes with authentication, lazy loading with React.lazy and Suspense, search params with useSearchParams, and 404 error handling. Create a navigation system worthy of the most advanced fleet traversing the Asteroid Belt!