Sketching out routes like this before writing code โ a habit worth building, echoing the planning advice from earlier component-design chapters โ makes the actual implementation far more straightforward.
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { useState } from "react";
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<BrowserRouter>
<NavBar isLoggedIn={isLoggedIn} />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/courses" element={<CourseList />} />
<Route path="/courses/:id" element={<CourseDetail />} />
<Route path="/login" element={<Login onLogin={() => setIsLoggedIn(true)} />} />
<Route
path="/dashboard"
element={isLoggedIn ? <Dashboard /> : <Navigate to="/login" />}
/>
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}The path="*" wildcard route always matches last, catching any URL that didn't match an earlier, more specific route โ this is the standard way to build a 404 Not Found page.
function NotFound() {
return (
<div>
<h2>404 - Page Not Found</h2>
<Link to="/">Go back home</Link>
</div>
);
}
function CourseDetail() {
const { id } = useParams();
return (
<div>
<nav><Link to="/">Home</Link> / <Link to="/courses">Courses</Link> / Course {id}</nav>
<h2>Course #{id}</h2>
</div>
);
}
Skipping a path="*" route means unmatched URLs render nothing at all, confusing users. Placing it before your other routes is equally broken โ React Router (v6+) matches top-to-bottom, so the wildcard would swallow every URL. Always place it last, as the final <Route>.
Extend the project structure above by adding a simple isLoggedIn-based conditional in the NavBar to show either a Login link or a Dashboard link.
function NavBar({ isLoggedIn }) {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/courses">Courses</Link>
{isLoggedIn ? (
<Link to="/dashboard">Dashboard</Link>
) : (
<Link to="/login">Login</Link>
)}
</nav>
);
}
Q Should isLoggedIn really just be a piece of useState like this?
For a learning project, yes โ this keeps the example focused on routing. A real production app would typically manage authentication state via Context (Chapter 24) or a dedicated auth library, so it's accessible app-wide without prop drilling.
Q Can nested routes share a common layout, like a persistent sidebar?
Yes โ React Router supports 'layout routes' using a parent Route with an