Table of Contents
- What is React Router?
- Installation and Setup
- Core Concepts
- Dynamic Routing with URL Parameters
- Nested Routes
- Protected Routes
- Query Parameters
- Programmatic Navigation
- Advanced Features
- Best Practices
- Conclusion
- References
What is React Router?
React Router is a lightweight, flexible library that enables client-side routing in React applications. Unlike server-side routing (where the server sends a new HTML page for each URL), client-side routing handles navigation internally, updating the browser’s URL and rendering the appropriate component without reloading the page. This results in faster, more app-like experiences.
Key Benefits of React Router:
- Single-Page Application (SPA) Support: Enables SPAs by dynamically rendering components based on the URL.
- Declarative Routing: Define routes using React components (e.g.,
<Route>), making code more readable and maintainable. - Flexible Navigation: Supports browser history (back/forward buttons), dynamic routes, nested routes, and protected routes.
- Accessibility: Built-in support for ARIA labels and semantic navigation via
<Link>.
Installation and Setup
To get started with React Router, install the react-router-dom package (for web applications). Use npm or yarn:
# Using npm
npm install react-router-dom
# Using yarn
yarn add react-router-dom
Basic Setup
After installation, wrap your app with a router. The most common choice is BrowserRouter, which uses the HTML5 history API to manage URLs.
Step 1: Wrap your app with BrowserRouter
In your root component (e.g., index.js or App.js):
import { BrowserRouter as Router } from 'react-router-dom';
function App() {
return (
<Router>
{/* Your app content (routes, navigation, etc.) */}
</Router>
);
}
export default App;
Step 2: Define Routes
Use <Routes> and <Route> components to define which component renders for a given URL.
Example: A simple app with Home and About pages:
import { Routes, Route, Link } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
function App() {
return (
<Router>
<nav>
{/* Navigation links */}
<Link to="/">Home</Link> |
<Link to="/about">About</Link>
</nav>
{/* Route definitions */}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
}
Here:
<Link>replaces<a>tags to navigate without reloading the page.<Routes>acts as a wrapper for<Route>elements and renders the first matching route.<Route path="/" element={<Home />}>renders theHomecomponent when the URL is/.
Core Concepts
1. BrowserRouter
BrowserRouter (aliased as Router in the example above) is the root component that enables routing. It must wrap all components that use React Router features (e.g., <Link>, <Route>).
2. Routes and Route
Routes: A container for<Route>elements. It scans its children<Route>components and renders the one with apathmatching the current URL.Route: Defines a mapping between a URL path and a component.
Props for <Route>:
path: The URL path (e.g.,/about). Use*for catch-all routes (e.g., 404 pages).element: The component to render when the path matches.index: A boolean flag to mark the “default” child route in nested routing (see Nested Routes).
3. Link and NavLink
-
Link: Used for navigation. It renders an<a>tag under the hood but prevents full page reloads.<Link to="/contact">Contact</Link> -
NavLink: A special version ofLinkthat adds styling when the current URL matches thetoprop. Use theactiveClassNameorstyleprop to customize active links:<NavLink to="/home" style={({ isActive }) => ({ color: isActive ? 'blue' : 'black', fontWeight: isActive ? 'bold' : 'normal', })} > Home </NavLink>
Dynamic Routing with URL Parameters
Dynamic routes let you capture values from the URL (e.g., user IDs, post slugs). Use :parameterName in the path prop to define dynamic segments.
Example: User Profile Page
Define a route with a dynamic id parameter:
<Routes>
<Route path="/users/:id" element={<UserProfile />} />
</Routes>
Access the parameter in the UserProfile component using the useParams hook:
import { useParams } from 'react-router-dom';
function UserProfile() {
const { id } = useParams(); // Extracts "id" from the URL
return <h1>User Profile: {id}</h1>;
}
If the URL is /users/123, id will be "123".
Nested Routes
Nested routes are used to render components inside other components (e.g., a dashboard with a sidebar and dynamic content area). Use the <Outlet> component to specify where child routes should render.
Example: Dashboard with Nested Routes
Step 1: Define parent and child routes
<Routes>
<Route path="/dashboard" element={<DashboardLayout />}>
{/* Child routes render inside DashboardLayout via Outlet */}
<Route index element={<DashboardHome />} /> {/* Default child */}
<Route path="settings" element={<DashboardSettings />} />
<Route path="profile" element={<DashboardProfile />} />
</Route>
</Routes>
Step 2: Use <Outlet> in the parent component (DashboardLayout)
import { Outlet, Link } from 'react-router-dom';
function DashboardLayout() {
return (
<div className="dashboard">
<aside>
<Link to="/dashboard">Home</Link>
<Link to="settings">Settings</Link> {/* Relative path */}
<Link to="profile">Profile</Link>
</aside>
<main>
<Outlet /> {/* Child routes render here */}
</main>
</div>
);
}
- The
indexprop marksDashboardHomeas the default child route for/dashboard. - Use relative paths (e.g.,
to="settings") instead of absolute paths (to="/dashboard/settings") for cleaner code.
Protected Routes
Protected routes restrict access to authenticated users (e.g., a dashboard). Create a wrapper component to check authentication status and redirect unauthenticated users.
Example: PrivateRoute Component
import { Navigate, Outlet } from 'react-router-dom';
// Mock authentication check (replace with real auth logic)
const isAuthenticated = () => {
return localStorage.getItem('token') !== null;
};
function PrivateRoute() {
if (!isAuthenticated()) {
// Redirect to login if not authenticated
return <Navigate to="/login" replace />;
}
// Render child routes if authenticated
return <Outlet />;
}
// Usage in routes
<Routes>
<Route path="/dashboard" element={<PrivateRoute />}>
{/* Nested protected routes */}
<Route index element={<DashboardHome />} />
<Route path="profile" element={<DashboardProfile />} />
</Route>
<Route path="/login" element={<Login />} />
</Routes>
<Navigate to="/login" replace />redirects to the login page and replaces the current history entry (prevents back-button navigation to the protected route).
Query Parameters
Query parameters (e.g., /search?query=react&page=1) are used to pass optional data in URLs. Access them with the useLocation hook and URLSearchParams.
Example: Search Page
import { useLocation } from 'react-router-dom';
function SearchPage() {
const location = useLocation();
const searchParams = new URLSearchParams(location.search);
const query = searchParams.get('query'); // "react"
const page = searchParams.get('page'); // "1"
return (
<div>
<h1>Search Results for: {query}</h1>
<p>Page: {page}</p>
</div>
);
}
To update query parameters programmatically, use useNavigate:
import { useNavigate } from 'react-router-dom';
function SearchBar() {
const navigate = useNavigate();
const handleSearch = (e) => {
e.preventDefault();
const query = e.target.query.value;
navigate(`/search?query=${query}`); // Updates URL with query
};
return (
<form onSubmit={handleSearch}>
<input name="query" placeholder="Search..." />
<button type="submit">Search</button>
</form>
);
}
Programmatic Navigation
Use the useNavigate hook to navigate programmatically (e.g., after form submission or authentication).
Basic Usage
import { useNavigate } from 'react-router-dom';
function Login() {
const navigate = useNavigate();
const handleLogin = () => {
// Mock login logic
localStorage.setItem('token', 'fake-token');
navigate('/dashboard'); // Navigate to dashboard after login
};
return <button onClick={handleLogin}>Login</button>;
}
Key Features of useNavigate:
- Replace History: Use
navigate('/dashboard', { replace: true })to replace the current history entry (avoids back-button loops). - Go Back/Forward:
navigate(-1)(back),navigate(1)(forward). - Pass State: Attach data to navigation (e.g.,
navigate('/success', { state: { message: 'Form submitted!' } })). Access state withuseLocation().state.
Advanced Features
1. useLocation
The useLocation hook returns the current location object, which contains:
pathname: The current URL path (e.g.,/about).search: Query parameters (e.g.,?query=react).state: Data passed during navigation.
Example: Track page views with useLocation:
import { useLocation, useEffect } from 'react-router-dom';
function Analytics() {
const location = useLocation();
useEffect(() => {
// Log page view when the URL changes
console.log(`Viewing: ${location.pathname}`);
}, [location.pathname]);
return null;
}
2. 404 Pages
Handle invalid URLs with a catch-all route (path="*"):
<Routes>
<Route path="/" element={<Home />} />
<Route path="*" element={<NotFound />} /> {/* Renders for unmatched paths */}
</Routes>
Best Practices
1. Organize Routes
Keep routes in a dedicated file (e.g., routes.js) for large apps:
// routes.js
import { Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
// ... other imports
export default function AppRoutes() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
{/* ... other routes */}
</Routes>
);
}
a2. Use Relative Paths
Prefer relative paths in nested routes to avoid hardcoding:
// Good: Relative path
<Link to="settings">Settings</Link>
// Bad: Hardcoded absolute path
<Link to="/dashboard/settings">Settings</Link>
3. Handle 404s
Always define a catch-all route for unmatched URLs to improve UX.
4. Avoid Deep Nesting
Deeply nested routes can complicate navigation. Split complex UIs into separate routes instead.
5. Test Routes
Use testing libraries like @testing-library/react to test route rendering and navigation logic.
Conclusion
React Router simplifies frontend development by providing a declarative, flexible way to manage navigation in React apps. From basic routing to dynamic parameters, nested routes, and protected routes, it handles all aspects of client-side routing, enabling you to build SPAs with seamless user experiences.
By following best practices like organizing routes, using relative paths, and handling edge cases (e.g., 404s), you can create maintainable, scalable applications. Whether you’re building a small blog or a large enterprise app, React Router is an essential tool in your frontend toolkit.