Difficulty: Intermediate
Dynamic routes allow you to define URL patterns with variable segments. Instead of creating a separate route for every user (/user/1, /user/2, /user/3), you define a single route with a parameter: /user/:id. The colon prefix (:) marks a segment as dynamic - it matches any value in that position and captures it as a named parameter. This is essential for resource-based URLs like user profiles, product pages, blog posts, and any content identified by an ID or slug.
In React Router v6, you access URL parameters using the useParams hook. This hook returns an object where keys are the parameter names from the route definition and values are the actual strings from the current URL. For the route /user/:id visited at /user/42, useParams returns { id: '42' }. Note that all parameter values are strings - if you need a number, you must parse it.
Multiple parameters can be used in a single route: /posts/:category/:postId matches /posts/react/42 and returns { category: 'react', postId: '42' }. This is useful for hierarchical URLs. Optional parameters are supported in v6 using a ? suffix: /posts/:category? matches both /posts and /posts/react.
A common pattern is fetching data based on URL parameters. The component reads the parameter with useParams, then uses it in a useEffect to fetch the corresponding data. When the URL changes (user navigates from /user/1 to /user/2), the parameter updates, the effect re-runs with the new ID, and the component shows the new data. This creates a clean separation between URL-driven state and component behavior.
Dynamic routes are matched with lower priority than static routes. React Router v6 automatically ranks routes so that /users/new (static) takes priority over /users/:id (dynamic). This means you can safely define both without worrying about order - /users/new will never accidentally match the :id parameter.
function extractParams(pattern, url) {
const patternParts = pattern.split('/');
const urlParts = url.split('/');
const params = {};
patternParts.forEach((part, i) => {
if (part.startsWith(':')) {
const paramName = part.slice(1);
params[paramName] = urlParts[i];
}
});
return params;
}
// Single parameter
let params = extractParams('/users/:id', '/users/42');
console.log('Route: /users/:id');
console.log('URL: /users/42');
console.log('Params:', JSON.stringify(params));
// Multiple parameters
params = extractParams('/posts/:category/:postId', '/posts/react/15');
console.log('Route: /posts/:category/:postId');
console.log('URL: /posts/react/15');
console.log('Params:', JSON.stringify(params));
React Router matches each segment of the URL against the route pattern. Segments prefixed with : are captured as named parameters. All parameter values are strings.
// Simulating useParams behavior
function useParams() {
// In real React Router, this reads from the router context
return { userId: '42', tab: 'settings' };
}
function UserProfile() {
const params = useParams();
console.log('User ID:', params.userId);
console.log('Active tab:', params.tab);
console.log('Param type:', typeof params.userId);
console.log('Need to parse for number:', typeof parseInt(params.userId));
// Common pattern: fetch data based on params
const numericId = parseInt(params.userId, 10);
console.log('Parsed ID:', numericId);
console.log('Would fetch user #' + numericId + ' from API');
return null;
}
UserProfile();
useParams returns all URL parameters as strings. For numeric operations, you must parse them with parseInt or Number. The component uses these parameters to determine what data to fetch and display.
// Simulating a component that fetches data based on URL params
function ProductPage(params) {
const { productId } = params;
console.log('ProductPage mounted with id:', productId);
// Simulate useEffect for data fetching
console.log('Fetching product:', productId);
const product = { id: productId, name: 'Product ' + productId, price: productId * 10 };
console.log('Product name:', product.name);
console.log('Product price: #39; + product.price);
return null;
}
// Simulating navigation between products
ProductPage({ productId: '5' });
console.log('---');
ProductPage({ productId: '12' });
When the URL changes from /product/5 to /product/12, useParams returns the new ID. A useEffect depending on the ID re-fetches data for the new product. The same component handles any product.
URL parameters, useParams, dynamic segments, parameterized routes