We use cookies to enhance your experience on the site
CodeWorlds

Complete Routing Architecture - Navigation Command Center

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:

  1. Authentication layer - AuthContext + ProtectedRoute
  2. Navigation layer - NavLink with active styles
  3. Routes layer - nested Routes with Outlet
  4. Parameters layer - dynamic paths with useParams
  5. 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 menu

Such 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!

Go to CodeWorlds→