React Router v6 (the current version) provides a modern, declarative approach to routing with improved performance, smaller bundle size, and a cleaner API compared to previous versions.
React Router enables: dynamic route matching, lazy component loading, location transition handling, dynamic route generation, and state preservation during navigation. It’s the industry standard for React routing.
Installation and Setup
Install React Router
Basic App Setup
Wrap your application with BrowserRouter to enable routing:
import { BrowserRouter } from ‘react-router-dom’;
import App from ‘./App’;
import ‘./index.css’;export default function Root() {
return (
<BrowserRouter>
<App />
</BrowserRouter>
);
}
Route Configuration
import { Routes, Route } from ‘react-router-dom’;
import Home from ‘./pages/Home’;
import About from ‘./pages/About’;
import NotFound from ‘./pages/NotFound’;export default function App() {
return (
<Routes>
<Route path=”/” element={<Home />} />
<Route path=”/about” element={<About />} />
<Route path=”*” element={<NotFound />} />
</Routes>
);
}
Basic Routing with Routes
Understanding Routes and Route
The Routes component renders the first Route that matches the current URL. Each Route defines a path and the component to render.
| Component | Purpose | Example |
|---|---|---|
| <Routes> | Container for all routes | <Routes>…</Routes> |
| <Route> | Define a single route | <Route path=”/” element={<Home />} /> |
| path | URL pattern to match | “/”, “/about”, “/posts/:id” |
| element | Component to render | element={<Home />} |
Route Matching Order
<Route path=”/posts/:id” element={<PostDetail />} />
<Route path=”/posts/new” element={<NewPost />} />
<Route path=”/posts” element={<PostList />} />
<Route path=”*” element={<NotFound />} />
</Routes>
Dynamic Routes with Parameters
Creating Dynamic Routes
Use colon syntax to create URL parameters:
<Route path=”/posts/:id” element={<PostDetail />} />
<Route path=”/users/:userId/posts/:postId” element={<UserPost />} />
Accessing Route Parameters with useParams
const { id } = useParams();return (
<div>
<h1>Post {id}</h1>
<p>This is the detail page for post {id}</p>
</div>
);
}
Multiple Parameters Example
export default function UserPost() {
const { userId, postId } = useParams();return (
<div>
<h1>User {userId}’s Post {postId}</h1>
<Link to={`/users/${userId}`}>
Back to User
</Link>
</div>
);
}
Query Parameters with useSearchParams
export default function PostList() {
const [searchParams, setSearchParams] = useSearchParams();const sort = searchParams.get(‘sort’);
const category = searchParams.get(‘category’);
return (
<div>
<p>Sort: {sort}, Category: {category}</p>
<button onClick={() => setSearchParams({ sort: ‘price’ })}>
Sort by Price
</button>
</div>
);
}
Nested Routes and Layouts
Creating Nested Routes
Organize related routes under a parent route:
return (
<Routes>
<Route path=”/” element={<Home />} />
<Route path=”/posts” element={<PostLayout />}>
<Route index element={<PostList />} />
<Route path=”:id” element={<PostDetail />} />
<Route path=”new” element={<NewPost />} />
</Route>
</Routes>
);
}
Layout Component with Outlet
Use Outlet to render child routes:
return (
<div className=”post-layout”>
<aside className=”sidebar”>
<Link to=”/posts”>All Posts</Link>
<Link to=”/posts/new”>New Post</Link>
</aside>
<main>
<Outlet />
</main>
</div>
);
}
Essential Router Hooks
useNavigate for Programmatic Navigation
Navigate programmatically after an action:
const navigate = useNavigate();const handleSubmit = async (e) => {
e.preventDefault();
// Save post…
navigate(‘/posts’);
// Or go back:
// navigate(-1);
};
return <form onSubmit={handleSubmit}>…</form>;
}
useLocation for Current URL
const location = useLocation();return (
<div>
<p>Current path: {location.pathname}</p>
<p>Search: {location.search}</p>
</div>
);
}
useMatch and useResolvedPath
const postMatch = useMatch(‘/posts/:id’);return (
<div>
{postMatch && <p>You are viewing post {postMatch.params.id}</p>}
</div>
);
}
| Hook | Purpose | Returns |
|---|---|---|
| useNavigate() | Programmatic navigation | Function to navigate |
| useParams() | Get URL parameters | Object with route params |
| useLocation() | Get current location info | Location object |
| useSearchParams() | Get/set query parameters | [params, setParams] |
| useMatch() | Check if pattern matches | Match object or null |
Advanced Routing Patterns
Protected Routes
Create private routes that require authentication:
if (!isAuthenticated) {
return <Navigate to=”/login” replace />;
}
return children;
}// Usage:
<Route
path=”/dashboard”
element={
<ProtectedRoute isAuthenticated={user !== null}>
<Dashboard />
</ProtectedRoute>
}
/>
Lazy Loading Routes
Code split routes for better performance:
import(‘./pages/HeavyComponent’)
);<Route
path=”/heavy”
element={
<Suspense fallback={<div>Loading…</div>}>
<HeavyComponent />
</Suspense>
}
/>
Redirects and Navigate
<Route path=”/old-about” element={<Navigate to=”/about” replace />} />// Or programmatically:
navigate(‘/new-path’, { replace: true });
Handling 404 Pages
return (
<div>
<h1>404 – Page Not Found</h1>
<p>The page you are looking for does not exist.</p>
<Link to=”/”>Go Home</Link>
</div>
);
}// In Routes (must be last):
<Route path=”*” element={<NotFound />} />
Best Practices
1. Keep Routes Organized
Create a separate file for route configuration:
import Home from ‘./pages/Home’;
import About from ‘./pages/About’;
import NotFound from ‘./pages/NotFound’;export const routes = [
{ path: ‘/’, element: <Home /> },
{ path: ‘/about’, element: <About /> },
{ path: ‘*’, element: <NotFound /> },
];
2. Use Relative Paths in Nested Routes
<Route path=”posts” element={<PostLayout />}>
<Route index element={<PostList />} />
<Route path=”:id” element={<PostDetail />} />
<Route path=”new” element={<NewPost />} />
</Route>
3. Handle Loading States
const [post, setPost] = useState(null);
const [loading, setLoading] = useState(true);
const { id } = useParams();useEffect(() => {
fetchPost(id).then(setPost).finally(() => setLoading(false));
}, [id]);if (loading) return <div>Loading…</div>;
if (!post) return <NotFound />;
return <article>{post.title}</article>;
}
4. Preserve Scroll Position
import { useLocation } from ‘react-router-dom’;export function ScrollToTop() {
const { pathname } = useLocation();useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
// Add to App:
<BrowserRouter>
<ScrollToTop />
<Routes>…</Routes>
</BrowserRouter>