In the previous module, we learned the basics of React Router as an interplanetary navigation system. Now we'll dive deeper into the details of routing configuration in different types of space applications, both small and complex multi-module systems.
React Router provides several types of routers that we can use depending on the environment in which our application runs:
BrowserRouter uses the HTML5 History API and is the most commonly used router in modern web applications. It creates clean, nice URLs without a hash (#):1import { BrowserRouter } from 'react-router-dom';
2
3function App() {
4 return (
5 <BrowserRouter>
6 {/* Routing components */}
7 </BrowserRouter>
8 );
9}Important:
BrowserRouter requires proper server configuration that must redirect all requests to the index.html file (to handle page refreshing and direct access to subpages).HashRouter uses the hash function in the URL (the part after #). This is a simpler solution that doesn't require server configuration, but creates less elegant URLs:1import { HashRouter } from 'react-router-dom';
2
3function App() {
4 return (
5 <HashRouter>
6 {/* Routing components */}
7 </HashRouter>
8 );
9}Example URLs when using HashRouter:
/#/mars, /#/jupiterMemoryRouter stores the navigation history in memory (doesn't use the browser URL). It's useful for tests and applications running in environments without a browser:1import { MemoryRouter } from 'react-router-dom';
2
3function App() {
4 return (
5 <MemoryRouter initialEntries={['/mars', '/jupiter']} initialIndex={1}>
6 {/* Routing components */}
7 </MemoryRouter>
8 );
9}The
Route component offers more options than just basic path-to-element mapping:1<Route path="/planet/:planetId" element={<PlanetDetails />} />This path matches URLs like
/planet/mars, /planet/jupiter, etc. The :planetId parameter is dynamic and can be read in the component.1<Route path="/missions/:year?/:month?" element={<MissionsList />} />Question marks indicate optional parameters. This path matches URLs:
/missions (without parameters)/missions/2150 (year only)/missions/2150/06 (year and month)1<Route path="/files/*" element={<FileExplorer />} />The asterisk catches everything after
/files/, e.g., /files/documents/reports/annual.pdf.In extensive space applications, routing can become complex. Here's how you can organize it:
We can nest routes to create a hierarchical structure:
1function AppRoutes() {
2 return (
3 <Routes>
4 <Route path="/" element={<SpaceLayout />}>
5 <Route index element={<Dashboard />} />
6 <Route path="planets" element={<PlanetLayout />}>
7 <Route index element={<PlanetsList />} />
8 <Route path=":planetId" element={<PlanetDetails />} />
9 </Route>
10 <Route path="missions" element={<MissionLayout />}>
11 <Route index element={<MissionsDashboard />} />
12 <Route path="scheduled" element={<ScheduledMissions />} />
13 <Route path="completed" element={<CompletedMissions />} />
14 </Route>
15 </Route>
16 <Route path="/auth" element={<AuthLayout />}>
17 <Route path="login" element={<Login />} />
18 <Route path="register" element={<Register />} />
19 </Route>
20 <Route path="*" element={<NotFound />} />
21 </Routes>
22 );
23}In the example above:
SpaceLayout is the main layout for paths starting with "/"PlanetLayout is the layout for all paths in the "planets" sectionindex element specifies what should be rendered for the parent path (e.g., "/planets")In React Router v6 we use the
element prop instead of component from earlier versions:1// React Router v6 (current)
2<Route path="/mars" element={<MarsBase />} />
3
4// React Router v5 (older)
5<Route path="/mars" component={MarsBase} />Why is this important? The
element syntax allows passing a rendered element, which provides greater flexibility:1<Route
2 path="/mars"
3 element={
4 <MarsBase
5 temperature={-60}
6 oxygenLevel="low"
7 waterAvailable={true}
8 />
9 }
10/>The
Outlet component specifies where in the parent component the sub-route components should be rendered:1import { Outlet } from 'react-router-dom';
2
3function SpaceLayout() {
4 return (
5 <div className="space-layout">
6 <header>
7 <h1>Interplanetary Navigation System</h1>
8 <nav>{/* Navigation links */}</nav>
9 </header>
10
11 <main>
12 {/* Sub-route components will be rendered here */}
13 <Outlet />
14 </main>
15
16 <footer>
17 <p>Space Command Center © 2150</p>
18 </footer>
19 </div>
20 );
21}A good practice is to add global error handling to our routing system:
1function AppRoutes() {
2 return (
3 <Routes>
4 <Route path="/" element={<SpaceLayout />} errorElement={<GlobalError />}>
5 {/* Other routes */}
6 </Route>
7 </Routes>
8 );
9}
10
11function GlobalError() {
12 const error = useRouteError();
13
14 return (
15 <div className="error-container">
16 <h1>Houston, we have a problem!</h1>
17 <p>An error occurred in the navigation system:</p>
18 <pre>{error.message || "Unknown error"}</pre>
19 <button onClick={() => window.location.href = "/"}>
20 Return to Command Center
21 </button>
22 </div>
23 );
24}Below is a comprehensive example of routing configuration for an advanced application:
1import {
2 BrowserRouter,
3 Routes,
4 Route,
5 Outlet,
6 Navigate,
7 useRouteError
8} from 'react-router-dom';
9
10// Layouts
11function MainLayout() {
12 return (
13 <div className="main-layout">
14 <MainNavigation />
15 <div className="content">
16 <Outlet />
17 </div>
18 <Footer />
19 </div>
20 );
21}
22
23function AuthLayout() {
24 return (
25 <div className="auth-layout">
26 <div className="auth-container">
27 <Outlet />
28 </div>
29 </div>
30 );
31}
32
33// Error Components
34function GlobalError() {
35 const error = useRouteError();
36 return (
37 <div className="error-screen">
38 <h1>Navigation System Error</h1>
39 <p>{error.message || "Unknown error"}</p>
40 </div>
41 );
42}
43
44function NotFound() {
45 return (
46 <div className="not-found">
47 <h1>Lost in Outer Space</h1>
48 <p>The ship could not find the requested location.</p>
49 </div>
50 );
51}
52
53// Protected Route Component
54function ProtectedRoute({ children }) {
55 const isAuthenticated = useAuth();
56
57 if (!isAuthenticated) {
58 return <Navigate to="/auth/login" replace />;
59 }
60
61 return children;
62}
63
64// Main App
65function App() {
66 return (
67 <BrowserRouter>
68 <Routes>
69 {/* Main section with layout */}
70 <Route path="/" element={<MainLayout />} errorElement={<GlobalError />}>
71 <Route index element={<Dashboard />} />
72
73 {/* Planets */}
74 <Route path="planets">
75 <Route index element={<PlanetsList />} />
76 <Route path=":planetId" element={<PlanetDetails />} />
77 </Route>
78
79 {/* Missions - require authentication */}
80 <Route path="missions" element={
81 <ProtectedRoute>
82 <MissionsLayout />
83 </ProtectedRoute>
84 }>
85 <Route index element={<MissionsDashboard />} />
86 <Route path="new" element={<NewMission />} />
87 <Route path=":missionId" element={<MissionDetails />} />
88 <Route path=":missionId/edit" element={<EditMission />} />
89 </Route>
90
91 {/* Crew */}
92 <Route path="crew">
93 <Route index element={<CrewList />} />
94 <Route path=":memberId" element={<CrewMemberProfile />} />
95 </Route>
96
97 {/* Settings - require authentication */}
98 <Route path="settings" element={
99 <ProtectedRoute>
100 <Settings />
101 </ProtectedRoute>
102 } />
103 </Route>
104
105 {/* Authentication section */}
106 <Route path="/auth" element={<AuthLayout />}>
107 <Route path="login" element={<Login />} />
108 <Route path="register" element={<Register />} />
109 <Route path="forgot-password" element={<ForgotPassword />} />
110 <Route path="reset-password/:token" element={<ResetPassword />} />
111 </Route>
112
113 {/* Public documentation */}
114 <Route path="/docs/*" element={<DocsLayout />} />
115
116 {/* Redirect from old path to new one */}
117 <Route path="/old-missions" element={<Navigate to="/missions" replace />} />
118
119 {/* 404 handling */}
120 <Route path="*" element={<NotFound />} />
121 </Routes>
122 </BrowserRouter>
123 );
124}When developing a space application, it's worth configuring development tools to work with React Router:
1// Only in development mode
2if (process.env.NODE_ENV === 'development') {
3 const ReactRouterDevTools = React.lazy(() =>
4 import('react-router-devtools').then(module => ({
5 default: module.ReactRouterDevTools
6 }))
7 );
8
9 // Usage in the application
10 <React.Suspense fallback={null}>
11 <ReactRouterDevTools />
12 </React.Suspense>
13}To handle page refreshing in the development environment with BrowserRouter, you need to configure webpack-dev-server:
1// webpack.config.js
2module.exports = {
3 // other settings...
4 devServer: {
5 historyApiFallback: true, // Redirect 404 to index.html
6 // other settings...
7 }
8};In the production environment, the server must be configured to handle all React Router paths:
1const express = require('express');
2const path = require('path');
3const app = express();
4
5// Serving static files
6app.use(express.static(path.join(__dirname, 'build')));
7
8// All requests that are not for static files are redirected to index.html
9app.get('*', (req, res) => {
10 res.sendFile(path.join(__dirname, 'build', 'index.html'));
11});
12
13const PORT = process.env.PORT || 3000;
14app.listen(PORT, () => {
15 console.log(`Server listening on port ${PORT}`);
16});1<IfModule mod_rewrite.c>
2 RewriteEngine On
3 RewriteBase /
4 RewriteRule ^index\.html$ - [L]
5 RewriteCond %{REQUEST_FILENAME} !-f
6 RewriteCond %{REQUEST_FILENAME} !-d
7 RewriteRule . /index.html [L]
8</IfModule>1server {
2 listen 80;
3 server_name space-application.com;
4
5 root /var/www/html;
6 index index.html;
7
8 location / {
9 try_files $uri $uri/ /index.html;
10 }
11}Proper routing configuration is crucial for the functioning of a space application (React). Key points:
BrowserRouter for modern applicationsOutlet for better organizationerrorElement for error handlingProtectedRoute component to protect accessWith a well-configured navigation system, users of your space application will be able to freely travel between its different parts, without worrying about getting lost in outer space (404 errors).