Introduction
Modern web applications often require dynamic routing, where different URLs map to different components or data. React, combined with React Router, provides robust tools for handling dynamic routes and URL parameters.
Dynamic routes are essential for scenarios like:
- User profile pages (
/user/:id) - Product pages (
/product/:productId) - Blog posts (
/post/:slug)
This article will cover:
- How to define dynamic segments in paths.
- How to access route parameters using
useParams. - A practical example of a user profile page fetching data by ID.
- Best practices for implementing dynamic routing in React.
Understanding Dynamic Routes
Dynamic routes are routes that contain placeholders, allowing parts of the URL to be variable. These placeholders are called route parameters.
Syntax
<Route path="/user/:id" element={<UserProfile />} />
:idis a dynamic segment.- Any value in that position of the URL will match this route.
useParamscan be used to access this value inside the component.
Defining Dynamic Routes
Using React Router v6, dynamic routes are easy to define.
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./Home";
import UserProfile from "./UserProfile";
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/user/:id" element={<UserProfile />} />
</Routes>
</Router>
);
}
export default App;
Explanation
/user/:idis a dynamic route whereidcan be any value.- React Router matches the path and renders the
UserProfilecomponent.
Accessing Route Parameters with useParams
The useParams hook allows you to access the values of dynamic segments in your component.
Example
import { useParams } from "react-router-dom";
function UserProfile() {
const { id } = useParams();
return (
<div>
<h1>User Profile</h1>
<p>User ID: {id}</p>
</div>
);
}
export default UserProfile;
Explanation
useParamsreturns an object containing all route parameters.idcorresponds to the dynamic segment:idin the route.
Example: User Profile Page Fetching Data by ID
In real applications, the dynamic ID is often used to fetch data from an API.
Step 1: User API Mock
// mockApi.js
export const users = [
{ id: "1", name: "Alice", email: "[email protected]" },
{ id: "2", name: "Bob", email: "[email protected]" },
{ id: "3", name: "Charlie", email: "[email protected]" }
];
export const getUserById = (id) =>
new Promise((resolve, reject) => {
const user = users.find((u) => u.id === id);
setTimeout(() => {
if (user) resolve(user);
else reject("User not found");
}, 500);
});
Step 2: UserProfile Component
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { getUserById } from "./mockApi";
function UserProfile() {
const { id } = useParams();
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
getUserById(id)
.then((data) => {
setUser(data);
setLoading(false);
})
.catch((err) => {
setError(err);
setLoading(false);
});
}, [id]);
if (loading) return <p>Loading...</p>;
if (error) return <p>{error}</p>;
return (
<div>
<h1>User Profile</h1>
<p>ID: {user.id}</p>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
</div>
);
}
export default UserProfile;
Step 3: Linking to Dynamic Routes
import React from "react";
import { Link } from "react-router-dom";
function Home() {
return (
<div>
<h1>Users</h1>
<ul>
<li><Link to="/user/1">Alice</Link></li>
<li><Link to="/user/2">Bob</Link></li>
<li><Link to="/user/3">Charlie</Link></li>
</ul>
</div>
);
}
export default Home;
Explanation
- Each user link navigates to a dynamic URL (
/user/:id). UserProfileusesuseParamsto fetch and display user data.
Nested Dynamic Routes
Dynamic routes can be nested for more complex structures, e.g., /user/:id/posts/:postId.
<Routes>
<Route path="/user/:id" element={<UserProfile />}>
<Route path="posts/:postId" element={<PostDetail />} />
</Route>
</Routes>
Accessing Nested Parameters
import { useParams } from "react-router-dom";
function PostDetail() {
const { id, postId } = useParams();
return (
<div>
<p>User ID: {id}</p>
<p>Post ID: {postId}</p>
</div>
);
}
Best Practices for Dynamic Routing
- Validate Route Parameters
- Always validate parameters before fetching data.
if (!/^\d+$/.test(id)) return <p>Invalid user ID</p>; - Use Meaningful Parameter Names
:userIdis more descriptive than:id.
- Avoid Deep Nesting if Possible
- Deeply nested dynamic routes can complicate navigation and state management.
- Fallback UI for Missing Data
- Show loading indicators and error messages for invalid IDs.
- Use Links Instead of Manual Navigation
- Use
Linkfromreact-router-domto navigate dynamically.
- Use
- Combine with Layout Routes
- Use parent layouts to share headers, sidebars, and other UI elements.
Example: Nested Dynamic Route with Layout
function UserLayout() {
return (
<div>
<h2>User Dashboard</h2>
<Outlet />
</div>
);
}
<Routes>
<Route path="/user/:id" element={<UserLayout />}>
<Route index element={<UserProfile />} />
<Route path="posts/:postId" element={<PostDetail />} />
</Route>
</Routes>
Outletrenders the child route content inside the layout.
Passing State with Navigation
You can pass additional state when navigating to a dynamic route:
<Link to={/user/1} state={{ fromHome: true }}>Alice</Link>
import { useLocation } from "react-router-dom";
function UserProfile() {
const location = useLocation();
console.log(location.state?.fromHome); // true
}
- Useful for tracking navigation context without using query parameters.
Combining Dynamic Routes with API Calls
- Use
useEffectto fetch data when parameters change. - Include the parameter in the dependency array:
[id]. - Handle loading and error states for better UX.
useEffect(() => {
fetchUserData(id)
.then(setUser)
.catch(setError)
.finally(() => setLoading(false));
}, [id]);
- This ensures the component updates whenever the route changes.
Leave a Reply