JavaScript and React course Β· Module 6: React Router
Redirects in Cosmic Navigation
In this lesson8
Imagine that the space station has changed its address: the old path /old-station leads nowhere, yet the crew still has it bookmarked. Or a tourist who isn't logged in tries to enter mission control. In both cases we don't want to show an empty page or an error. We want the ship to change course by itself.
In interstellar space, we sometimes need to immediately redirect our ship to another course. In the world of React Router, redirects work similarly - they allow us to transfer the user from one path to another automatically.
The examples in this lesson work the same way in React Router v6 and v7. In v7 you can import the same components and hooks from the react-router package, and react-router-dom still provides them, so the imports in this lesson remain correct. Version 8, released in June 2026, no longer includes the react-router-dom package: there you import the same components and hooks from react-router, and the code of the examples itself does not change.
The Navigate Component
The simplest way to create a redirect is to use the Navigate component from React Router:
1import { Navigate } from 'react-router-dom';
2
3function RedirectToControl() {
4 return <Navigate to="/control-center" />;
5}When this component is rendered, the user will be automatically redirected to the specified path. This is a declarative way of redirecting - you simply place the component in the JSX tree.
Redirecting an old route
The simplest scenario is a route that has changed its address. The setup takes four steps: you define the source route, give it Navigate with the new address as its element, add replace, and React Router moves the user automatically:
1<Routes>
2 <Route path="/old-station" element={<Navigate to="/space-station" replace />} />
3 <Route path="/space-station" element={<SpaceStation />} />
4</Routes>Whoever types the old address lands on /space-station, and no trace of the old path remains in the browser history. The SpaceStation component knows nothing about the redirect and needs no changes.
Conditional Redirects
We often need to redirect the user only under certain conditions, for example when they are not logged in. The useAuth hook in the examples is our own hook (not part of React Router) that returns information about the logged-in user:
1function SecretMoonBase() {
2 const isAstronaut = useAuth();
3
4 if (!isAstronaut) {
5 return <Navigate to="/access-denied" replace />;
6 }
7
8 return (
9 <div>
10 <h1>Secret Moon Base</h1>
11 <p>Welcome astronaut! You have access to classified data.</p>
12 </div>
13 );
14}The replace attribute causes the redirect to replace the current page in the browser history, instead of adding a new one. This way the user won't end up back on the protected page after clicking the "back" button.
Programmatic Redirects
Sometimes we need to redirect the user in response to some event, like a form submission. We use the useNavigate hook for this:
1import { useNavigate } from 'react-router-dom';
2
3function LaunchControlForm() {
4 const navigate = useNavigate();
5
6 const handleLaunch = (event) => {
7 event.preventDefault();
8 // Launch preparation logic...
9
10 // Redirect to the countdown page
11 navigate('/countdown');
12
13 // Or with the replace parameter
14 // navigate('/countdown', { replace: true });
15 };
16
17 return (
18 <form onSubmit={handleLaunch}>
19 <h2>Launch Control Panel</h2>
20 <button type="submit">Start Countdown</button>
21 </form>
22 );
23}We call the navigate function in an event handler, not directly in the component body. React Router warns you if you call it during the first render. Where the decision is made during rendering, the Navigate component is the better fit.
These two tools are the ends of one scale. From the most declarative to imperative: Link (a plain link in JSX), NavLink (a link with an active state), Navigate (a redirect in JSX) and useNavigate (a hook called in code).
Handling Non-existent Paths (404)
React Router allows us to easily handle the case when a user tries to visit a path that doesn't exist:
1<Routes>
2 <Route path="/mission-control" element={<MissionControl />} />
3 <Route path="/navigation" element={<Navigation />} />
4 <Route path="*" element={<Navigate to="/lost-in-space" />} />
5</Routes>The * path matches any route that doesn't match any of the previous ones. Instead of Navigate, we can also render a 404 component:
1<Route path="*" element={<LostInSpace />} />I recommend the second option. A "lost in space" page keeps the wrong URL in the address bar, so the user sees what they typed and can correct it.
Redirects with Parameters
We can also pass additional data during a redirect. The state option does this, and it is available both in the component and in the navigate function:
1// Declarative redirect with parameters
2<Navigate
3 to="/asteroid-alert"
4 state={{ severity: "high", location: "sector-7" }}
5/>
6
7// Programmatic redirect with parameters
8navigate('/asteroid-alert', {
9 state: { severity: "high", location: "sector-7" }
10});Data from state doesn't go into the URL, so the address stays clean. In the target component, we can read this data using the useLocation hook:
1import { useLocation } from 'react-router-dom';
2
3function AsteroidAlert() {
4 const location = useLocation();
5 const { severity, location: asteroidLocation } = location.state || {};
6
7 return (
8 <div className="alert">
9 <h1>Asteroid Warning!</h1>
10 {severity && asteroidLocation && (
11 <p>
12 Asteroid detected with {severity} threat level at location {asteroidLocation}.
13 </p>
14 )}
15 </div>
16 );
17}Note the location.state || {}. If someone opens the page directly from the address bar, state will be empty, and this guard protects the component from an error.
Redirects with Authentication
The most common use case for redirects is protecting routes from unauthorized users. The ProtectedRoute component reads the current location with the useLocation hook to remember where the user wanted to fly:
1function ProtectedRoute({ children }) {
2 const { user, loading } = useAuth();
3 const location = useLocation();
4
5 if (loading) {
6 return <LoadingSpinner />;
7 }
8
9 if (!user) {
10 // Redirect to login page saving the original path
11 return <Navigate to="/login" state={{ from: location.pathname }} />;
12 }
13
14 return children;
15}
16
17// Usage:
18<Routes>
19 <Route path="/mission-control" element={
20 <ProtectedRoute>
21 <MissionControl />
22 </ProtectedRoute>
23 } />
24</Routes>The protected MissionControl component stays unchanged, the wrapper holds all the access logic. On the login page, we can read the original path and redirect back after login:
1function Login() {
2 const navigate = useNavigate();
3 const location = useLocation();
4 const from = location.state?.from || '/dashboard';
5
6 const handleLogin = async (credentials) => {
7 const success = await loginUser(credentials);
8
9 if (success) {
10 // Redirect back to the original page
11 navigate(from, { replace: true });
12 }
13 };
14
15 // Rest of the component...
16}Here replace: true removes the login page from the history, so the "back" button won't take the logged-in astronaut back to the form.
In React Router's data mode (since v6.4, also in v7) redirects are often performed already in loader and action functions using the redirect function, before the component renders at all. You will meet this with more advanced routing architecture.
Summary
Redirects are a powerful tool in React applications using React Router. They allow for:
- Automatically moving users between paths
- Creating protected areas requiring authentication
- Handling non-existent paths (404)
- Passing data between pages during a redirect
Remember to use the replace parameter when you want to prevent the user from returning to the previous page using the "back" button - this is especially important in the case of login pages and other authentication scenarios.
You already met route protection in the previous lesson, and in the next one you will see how lazy loading loads the ship's modules only when they are needed.
Remember: Navigate changes course during rendering, useNavigate after an event, and replace cleans up the traces in the flight log.
Code for this lesson: App.jsx
1import React from 'react';
2import { BrowserRouter, Routes, Route, Link, Outlet, useParams } from 'react-router-dom';
3
4// Layout for the Fleet section
5function FleetLayout() {
6 return (
7 <div className="fleet-layout">
8 <aside className="fleet-nav">
9 <h2>Fleet</h2>
10 <Link to="/fleet/alpha">Alpha Squadron</Link>
11 <Link to="/fleet/beta">Beta Squadron</Link>
12 <Link to="/fleet/gamma">Gamma Squadron</Link>
13 </aside>
14 <main className="fleet-content">
15 {/* Outlet renders nested routes */}
16 <Outlet />
17 </main>
18 </div>
19 );
20}
21
22// Fleet home page
23function FleetHome() {
24 return (
25 <div className="page">
26 <h1>Choose a squadron</h1>
27 <p>Click on a squadron in the menu to see details.</p>
28 </div>
29 );
30}
31
32// Squadron details
33function SquadronDetails() {
34 const { squadronId } = useParams();
35
36 return (
37 <div className="page squadron">
38 <h1>Squadron: {squadronId?.toUpperCase()}</h1>
39
40 <div className="squadron-nav">
41 <Link to={`/fleet/${squadronId}/ships`}>Ships</Link>
42 <Link to={`/fleet/${squadronId}/crew`}>Crew</Link>
43 <Link to={`/fleet/${squadronId}/missions`}>Missions</Link>
44 </div>
45
46 {/* Another Outlet for deeper nested routes */}
47 <Outlet />
48 </div>
49 );
50}
51
52// Nested components for squadron
53function SquadronShips() {
54 const { squadronId } = useParams();
55 return (
56 <div className="sub-page">
57 <h2>Squadron {squadronId} ships</h2>
58 <ul>
59 <li>Fighter-01</li>
60 <li>Fighter-02</li>
61 <li>Cruiser-01</li>
62 </ul>
63 </div>
64 );
65}
66
67function SquadronCrew() {
68 const { squadronId } = useParams();
69 return (
70 <div className="sub-page">
71 <h2>Squadron {squadronId} crew</h2>
72 <ul>
73 <li>Captain: John Smith</li>
74 <li>Pilot: Anna Kowalska</li>
75 <li>Engineer: Mark Johnson</li>
76 </ul>
77 </div>
78 );
79}
80
81function SquadronMissions() {
82 const { squadronId } = useParams();
83 return (
84 <div className="sub-page">
85 <h2>Squadron {squadronId} missions</h2>
86 <ul>
87 <li>Sector 7 Patrol - In progress</li>
88 <li>Transport escort - Completed</li>
89 </ul>
90 </div>
91 );
92}
93
94function App() {
95 return (
96 <BrowserRouter>
97 <div className="app">
98 <header>
99 <Link to="/">Home</Link>
100 <Link to="/fleet">Fleet</Link>
101 </header>
102
103 <Routes>
104 <Route path="/" element={<div className="page home"><h1>Space Command</h1></div>} />
105
106 {/* Nested routes */}
107 <Route path="/fleet" element={<FleetLayout />}>
108 <Route index element={<FleetHome />} />
109 <Route path=":squadronId" element={<SquadronDetails />}>
110 <Route path="ships" element={<SquadronShips />} />
111 <Route path="crew" element={<SquadronCrew />} />
112 <Route path="missions" element={<SquadronMissions />} />
113 </Route>
114 </Route>
115 </Routes>
116 </div>
117 </BrowserRouter>
118 );
119}
120
121export default App;Spotted a mistake in this lesson?
Hands-on tasks in the game
- Vertical ordering
Arrange the steps for configuring a redirect in React Router v6:
- Vertical ordering
Arrange the process of rendering a nested route in React Router:
- Code editor
Dynamic data loading with params
- Click in order
Arrange the programmatic navigation code after login:
- Vertical ordering
Arrange React Router navigation methods from declarative to imperative: