Dynamic Routes and URL Parameters in React Router

Create dynamic routes in React Router with colon segments like :productId, then read the matched value with the useParams hook.

5 min read

Dynamic routes in React Router match a whole family of URLs with one route. A segment that starts with a colon, like :productId, captures that part of the URL. The useParams hook reads the captured value inside the component.

For a shop, one route serves /products/1, /products/2, and every other id.

Declare a dynamic segment

Add a colon segment to the route path, and React Router captures whatever appears in that position. The name after the colon becomes the parameter key.

App.jsxApp.jsx
<Routes>
  <Route path="products" element={<Products />} />
  <Route path="products/:productId" element={<Product />} />
</Routes>

The second route matches /products/1, /products/42, and any other value in that position. The static products route still matches /products on its own.

Read the parameter with useParams

Inside the matched component, call useParams. It returns an object whose keys are the segment names from the path.

App.jsxApp.jsx
import { useParams } from "react-router";
 
export default function Product() {
  const { productId } = useParams();
  return <h1>Product {productId}</h1>;
}

For the URL /products/42, productId is the string "42". Every parameter arrives as a string, so convert with Number(productId) before doing math or comparisons.

One component now serves every product instead of writing a route per id, and the id arrives through the URL so the page is shareable.

Build the target path from data. A Link whose to prop contains the id opens that product.

App.jsxApp.jsx
import { Link } from "react-router";
 
export default function ProductLink({ id, name }) {
  return <Link to={`/products/${id}`}>{name}</Link>;
}

The to prop becomes /products/1 when id is 1. This is how a list page turns a row of products into a set of links, and it pairs with programmatic navigation with useNavigate. Any data source works here, including fetched records, so the list and the detail page stay in sync through the URL.

Multiple parameters and catch-all segments

A route can capture several segments at once. This path exposes two keys from useParams.

App.jsxApp.jsx
<Route path="posts/:postId/comments/:commentId" element={<Comment />} />

Inside Comment, both postId and commentId are available. A catch-all segment ending in * captures everything after the slash, including more slashes, which suits file browsers and nested paths.

Keep segment names unique in one path. A repeated name lets the later value overwrite the earlier one in the params object.

Dynamic segments also work inside nested routes, where a child captures a changing value after the parent prefix. That combination is covered in how to create nested routes with React Router.

Common mistakes

Most dynamic route bugs come from treating a URL string as a number or from a missing colon.

  • Reading a parameter as a number without converting it first.
  • Forgetting the colon, which turns the segment into a literal path instead of a parameter.
  • Using the array index as a key for a list of dynamically linked items.
Rune AI

Rune AI

Key Insights

  • Add a colon segment to the route path to capture part of the URL.
  • Read parameters with the useParams hook.
  • Convert parameter strings before using them as numbers.
  • Build dynamic links by inserting values into the to prop.
RunePowered by Rune AI

Frequently Asked Questions

What type does useParams return?

Every value is a string, because it comes from the URL. Convert with Number or parse it before doing math or comparisons.

Can one route capture multiple parameters?

Yes. A path like /posts/:postId/comments/:commentId exposes both postId and commentId from useParams.

What is a catch-all segment?

A segment ending in * captures everything after that slash, including more slashes. It is useful for file browsers and nested paths.

Conclusion

Dynamic routes match many URLs with one path. Add a colon segment like :productId, read it with useParams, and build links by inserting values into the path. Remember that parameters always arrive as strings.