Setting up React Router is the first concrete step in giving the MERN Blog real navigation. In this lesson you will install React Router v6, wrap the application in BrowserRouter, define the complete route map using Routes and Route, and verify that navigating between URLs renders the correct page component. By the end you will have a fully routed SPA with a consistent layout, a working 404 page, and all the route slots ready to receive real content in the chapters ahead.
Installation
cd client
npm install react-router-dom
Step 1 — Wrap the App in BrowserRouter
// src/main.jsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App.jsx';
import './index.css';
createRoot(document.getElementById('root')).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);
Note:
BrowserRouter must wrap the entire application — typically in main.jsx, not in App.jsx. All React Router components and hooks (Link, Route, useNavigate, useParams) only work inside a component that is a descendant of BrowserRouter. If you try to use a React Router hook outside the BrowserRouter context, React throws an error: “useNavigate() may be used only in the context of a Router component.”Tip: React Router v6 uses
Routes (plural) as the container for route definitions, and the Route component takes an element prop (not a component prop as in v5). The element prop accepts JSX — element={<HomePage />} — not a reference to the component function. This means you pass props directly to the page component right there in the route definition, which is much more flexible than v5’s approach.Warning: In React Router v6, the
Routes component renders only the first matching route — there is no need for the exact prop that was required in v5. Route matching in v6 is always exact by default. The order of Route components inside Routes still matters for catch-alls: put the wildcard path="*" route last.Step 2 — Define the Route Map in App.jsx
// src/App.jsx
import { Routes, Route } from 'react-router-dom';
import PageLayout from '@/components/layout/PageLayout';
import HomePage from '@/pages/HomePage';
import PostDetail from '@/pages/PostDetailPage';
import LoginPage from '@/pages/LoginPage';
import RegisterPage from '@/pages/RegisterPage';
import DashboardPage from '@/pages/DashboardPage';
import CreatePostPage from '@/pages/CreatePostPage';
import EditPostPage from '@/pages/EditPostPage';
import NotFoundPage from '@/pages/NotFoundPage';
import ProtectedRoute from '@/components/auth/ProtectedRoute';
function App() {
return (
<PageLayout>
<Routes>
{/* Public routes */}
<Route path="/" element={<HomePage />} />
<Route path="/posts/:id" element={<PostDetail />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
{/* Protected routes — wrapped in ProtectedRoute */}
<Route path="/dashboard" element={<ProtectedRoute><DashboardPage /></ProtectedRoute>} />
<Route path="/posts/new" element={<ProtectedRoute><CreatePostPage /></ProtectedRoute>} />
<Route path="/posts/:id/edit" element={<ProtectedRoute><EditPostPage /></ProtectedRoute>} />
{/* 404 catch-all — must be last */}
<Route path="*" element={<NotFoundPage />} />
</Routes>
</PageLayout>
);
}
export default App;
Step 3 — Create Page Stub Components
// src/pages/HomePage.jsx
function HomePage() {
return (
<div className="home-page">
<h1>Latest Posts</h1>
<p>Posts will appear here when connected to the API.</p>
</div>
);
}
export default HomePage;
// src/pages/LoginPage.jsx
function LoginPage() {
return <div><h1>Login</h1><p>Login form goes here.</p></div>;
}
export default LoginPage;
// src/pages/RegisterPage.jsx
function RegisterPage() {
return <div><h1>Register</h1><p>Register form goes here.</p></div>;
}
export default RegisterPage;
// src/pages/NotFoundPage.jsx
import { Link } from 'react-router-dom';
function NotFoundPage() {
return (
<div className="not-found">
<h1>404 — Page Not Found</h1>
<p>The page you are looking for does not exist.</p>
<Link to="/">← Back to Home</Link>
</div>
);
}
export default NotFoundPage;
Nested Routes with Layout — Outlet Pattern
// Alternative: layout routes with Outlet
// Useful when some routes share a sub-layout (e.g. dashboard has its own sidebar)
// src/pages/DashboardLayout.jsx
import { Outlet } from 'react-router-dom';
function DashboardLayout() {
return (
<div className="dashboard">
<aside className="dashboard__sidebar">
<nav>
<Link to="/dashboard">My Posts</Link>
<Link to="/dashboard/settings">Settings</Link>
</nav>
</aside>
<main className="dashboard__content">
<Outlet /> {/* matched child route renders here */}
</main>
</div>
);
}
// App.jsx — nested routes
<Route path="/dashboard" element={<ProtectedRoute><DashboardLayout /></ProtectedRoute>}>
<Route index element={<MyPostsPage />} /> {/* /dashboard */}
<Route path="settings" element={<SettingsPage />} /> {/* /dashboard/settings */}
</Route>
Common Mistakes
Mistake 1 — Defining routes in the wrong order
❌ Wrong — catch-all before specific routes:
<Routes>
<Route path="*" element={<NotFound />} /> {/* too early! */}
<Route path="/posts/:id" element={<PostDetail />} /> {/* never reached */}
</Routes>
✅ Correct — wildcard always last:
<Routes>
<Route path="/posts/:id" element={<PostDetail />} />
<Route path="*" element={<NotFound />} /> {/* ✓ last */}
</Routes>
Mistake 2 — Not wrapping App in BrowserRouter
❌ Wrong — using React Router components without a provider:
// main.jsx — BrowserRouter missing
createRoot(document.getElementById('root')).render(<App />);
// Error: useNavigate() may be used only in the context of a Router component
✅ Correct — BrowserRouter wraps the app in main.jsx.
Mistake 3 — Using the v5 component prop instead of v6 element prop
❌ Wrong — v5 API in a v6 project:
<Route path="/" component={HomePage} /> // v5 — does not work in v6
✅ Correct — v6 uses element with JSX:
<Route path="/" element={<HomePage />} /> // ✓
Quick Reference
| Task | v6 Code |
|---|---|
| Install | npm install react-router-dom |
| Wrap app | <BrowserRouter><App /></BrowserRouter> in main.jsx |
| Route container | <Routes>...</Routes> |
| Define a route | <Route path="/path" element={<Page />} /> |
| Index route | <Route index element={<Home />} /> |
| Catch-all 404 | <Route path="*" element={<NotFound />} /> |
| Nested layout | Parent Route with child Routes and <Outlet /> |