react-router-rutas-dinamicas

Dynamic Routes in React Router

  • 4 min

So far we have defined static routes: / is the Home, /about is About. A 1 to 1 relationship.

Now think about an online store with 10,000 products. Are we going to write 10,000 <Route> lines by hand? path="/product/shoe-1", path="/product/shoe-2"

That’s a no. What we need is to define a Dynamic Route: a single route that can capture a variable value from the URL and use it to decide what to display.

Defining the parameter (:)

In React Router, to indicate that a part of the URL is a variable parameter, we use the colon : syntax.

When writing path="/products/:id", we are telling the Router:

Any URL starting with /products/ followed by something matches here. And store that ‘something’ in a variable called id.

Let’s modify our main routes file:

// src/App.jsx
import { Routes, Route } from 'react-router';
import ProductList from './pages/ProductList';
import ProductDetail from './pages/ProductDetail';

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/products" element={<ProductList />} />
      
      {/* 👇 Here is the parameter: :id */}
      <Route path="/products/:id" element={<ProductDetail />} />
    </Routes>
  );
}
Copied!

This will match:

  • /products/123
  • /products/iphone-15
  • /products/abc-xyz

The useParams Hook

Now that the route captures the value, we need to access it from within the <ProductDetail /> component. For this, React Router offers us the hook useParams.

This hook returns an object with key/value pairs of all dynamic parameters from the current URL.

// src/pages/ProductDetail.jsx
import { useParams } from 'react-router';

export default function ProductDetail() {
  // 1. Extract the "id" parameter (must match what's in the Route)
  const { id } = useParams();

  return (
    <div>
      <h1>You are viewing the product with ID: {id}</h1>
      {/* Here we would use this ID to request data from an API */}
    </div>
  );
}
Copied!

Real-world example: List and detail

Let’s see the complete flow. First, a list of links, and then the detail page that uses the ID.

The list

In the product list, we use the Link component, constructing the URL dynamically.

// src/pages/ProductList.jsx
import { Link } from 'react-router';

const products = [
  { id: 1, name: 'Gaming Laptop' },
  { id: 2, name: 'Mechanical Keyboard' },
  { id: 3, name: 'Wireless Mouse' }
];

export default function ProductList() {
  return (
    <div>
      <h2>Catalog</h2>
      <ul>
        {products.map((prod) => (
          <li key={prod.id}>
            {/* Build the URL: /products/1, /products/2... */}
            <Link to={`/products/${prod.id}`}>
              View {prod.name}
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}
Copied!

The detail

Now, in the detail component, we use that id to find the correct information. In a real app, we would make a fetch to an API, but here we’ll simulate a local database.

// src/pages/ProductDetail.jsx
import { useParams, Link } from 'react-router';

// Simulated "database"
const database = [
  { id: 1, name: 'Gaming Laptop', price: 1200, desc: 'Pure power' },
  { id: 2, name: 'Mechanical Keyboard', price: 80, desc: 'Clicky clicky' },
  { id: 3, name: 'Wireless Mouse', price: 40, desc: 'No cables' }
];

export default function ProductDetail() {
  const { id } = useParams();

  // ⚠️ IMPORTANT: useParams ALWAYS returns strings.
  // If your IDs are numeric, you must convert them.
  const product = database.find(item => item.id === Number(id));

  // Handling "Not found" case
  if (!product) {
    return <h2>Product not found 😢</h2>;
  }

  return (
    <div className="detail">
      <h1>{product.name}</h1>
      <p>Price: {product.price}</p>
      <p>Description: {product.desc}</p>
      
      <Link to="/products">⬅ Back to list</Link>
    </div>
  );
}
Copied!

URL parameters are always string. Even if the URL is /products/5, useParams will return { id: "5" }.

If you do item.id === id (strict comparison with a number), it will fail. Use Number(id) or parseInt(id).