1. What Is Routing & Client-Side Routing?
In traditional multi-page websites, every link click triggers an HTTP request to the web server, which sends back an entirely new HTML document—causing the entire browser screen to blink and reload.
In a React Single-Page Application (SPA), Client-Side Routing intercepts browser URL changes using the HTML5 History API (pushState) and dynamically mounts the corresponding React component—without requesting a new HTML page!
3. Setting Up React Router DOM
Install the official package from npm:
Wrap your root application with <BrowserRouter> and define routes inside <Routes>:
import { Home } from './pages/Home';
import { About } from './pages/About';
export function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}
5. Navigation: Link, NavLink & useNavigate
| Tool | Usage | Primary Purpose |
|---|---|---|
<Link to="/path"> | Declarative in JSX | Client-side link navigation without browser reload |
<NavLink to="/path"> | Active-styled navigation | Provides isActive boolean to highlight current tab |
useNavigate() | Programmatic in functions | Imperative redirect after login, submit, or timer |
export function Navbar() {
const navigate = useNavigate();
return (
<nav>
<NavLink to="/" className={({ isActive }) => isActive ? 'active' : ''}>Home</NavLink>
<Link to="/about">About</Link>
<button onClick={() => navigate('/dashboard')}>Go to Dashboard</button>
</nav>
);
}
6. Route Parameters & useParams
Use a colon : to define dynamic parameters in your route path. In the target component, read the values using useParams():
<Route path="/products/:productId" element={<ProductDetail />} />
// ProductDetail.jsx
import { useParams } from 'react-router-dom';
export function ProductDetail() {
const { productId } = useParams();
return <h2>Showing details for product #{productId}</h2>;
}
7. Nested Routes, Layouts & <Outlet />
Nested routing allows child components to share a parent layout (e.g. persistent sidebar, navigation bar) without re-mounting the layout on page change:
<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardOverview />} />
<Route path="settings" element={<Settings />} />
<Route path="profile" element={<Profile />} />
</Route>
// DashboardLayout.jsx
import { Outlet, Link } from 'react-router-dom';
export function DashboardLayout() {
return (
<div className="dashboard">
<aside>
<Link to="/dashboard">Overview</Link>
<Link to="/dashboard/settings">Settings</Link>
</aside>
<main>
<Outlet />
</main>
</div>
);
}
9. Handling 404 Fallback Pages
Use the wildcard path="*" at the bottom of your route definitions to capture any URL that does not match predefined rules:
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
10. Query Parameters with useSearchParams
Query strings like /courses?category=react&sort=asc are read and modified using useSearchParams():
export function CourseFilter() {
const [searchParams, setSearchParams] = useSearchParams();
const category = searchParams.get("category") || "all";
return (
<button onClick={() => setSearchParams({ category: "react" })}>
Filter React (Current: {category})
</button>
);
}
12. Protected Routes (Authentication Guards)
Guard private pages (like dashboards or admin consoles) from unauthenticated users:
export function ProtectedRoute({ isAuthenticated }) {
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return <Outlet />;
}
14. Common Mistakes & Best Practices
- Using <a href> instead of <Link to>: Causes full page reloads and state resets.
- Forgetting <Outlet /> in nested layouts: Child components will fail to display.
- Leading slash in nested child paths: Child routes under
/dashboardshould be defined aspath="settings"(relative), notpath="/settings". - Organize route trees hierarchically: Group related routes under shared layouts.