Hasta ahora hemos definido rutas estáticas: / es la Home, /about es About. Una relación 1 a 1.
Ahora pensad en una tienda online con 10.000 productos. ¿Vamos a escribir 10.000 líneas <Route> a mano? path="/producto/zapato-1", path="/producto/zapato-2"…
Va a ser que no. Lo que necesitamos es definir una Ruta Dinámica: una única ruta que sea capaz de capturar un valor variable de la URL y usarlo para decidir qué mostrar.
Definiendo el parámetro (:)
En React Router, para indicar que una parte de la URL es un parámetro variable, utilizamos la sintaxis de dos puntos :.
Al escribir path="/productos/:id", le estamos diciendo al Router:
Cualquier URL que empiece por /productos/ seguido de algo, encaja aquí. Y ese ‘algo’ guárdalo en una variable llamada
id.
Vamos a modificar nuestro archivo de rutas principal:
// src/App.jsx
import { Routes, Route } from 'react-router';
import ListaProductos from './pages/ListaProductos';
import DetalleProducto from './pages/DetalleProducto';
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/productos" element={<ListaProductos />} />
{/* 👇 Aquí está el parámetro: :id */}
<Route path="/productos/:id" element={<DetalleProducto />} />
</Routes>
);
}
Esto encajará con:
/productos/123/productos/iphone-15/productos/abc-xyz
El Hook useParams
Ahora que la ruta captura el valor, necesitamos acceder a él desde dentro del componente <DetalleProducto />. Para ello, React Router nos ofrece el hook useParams.
Este hook devuelve un objeto con pares clave/valor de todos los parámetros dinámicos de la URL actual.
// src/pages/DetalleProducto.jsx
import { useParams } from 'react-router';
export default function DetalleProducto() {
// 1. Extraemos el parámetro "id" (debe coincidir con lo puesto en el Route)
const { id } = useParams();
return (
<div>
<h1>Estás viendo el producto con ID: {id}</h1>
{/* Aquí usaríamos este ID para pedir datos a una API */}
</div>
);
}
Ejemplo real: Listado y detalle
Vamos a ver el flujo completo. Primero, una lista de enlaces, y luego la página de detalle que usa el ID.
La lista
En la lista de productos, usamos el componente Link construyendo la URL dinámicamente.
// src/pages/ListaProductos.jsx
import { Link } from 'react-router';
const productos = [
{ id: 1, nombre: 'Laptop Gamer' },
{ id: 2, nombre: 'Teclado Mecánico' },
{ id: 3, nombre: 'Ratón Inalámbrico' }
];
export default function ListaProductos() {
return (
<div>
<h2>Catálogo</h2>
<ul>
{productos.map((prod) => (
<li key={prod.id}>
{/* Construimos la URL: /productos/1, /productos/2... */}
<Link to={`/productos/${prod.id}`}>
Ver {prod.nombre}
</Link>
</li>
))}
</ul>
</div>
);
}
El detalle
Ahora, en el componente de detalle, usamos ese id para buscar la información correcta. En una app real haríamos un fetch a una API, pero aquí simularemos una base de datos local.
// src/pages/DetalleProducto.jsx
import { useParams, Link } from 'react-router';
// "Base de datos" simulada
const baseDeDatos = [
{ id: 1, nombre: 'Laptop Gamer', precio: 1200, desc: 'Potencia pura' },
{ id: 2, nombre: 'Teclado Mecánico', precio: 80, desc: 'Clicky clicky' },
{ id: 3, nombre: 'Ratón Inalámbrico', precio: 40, desc: 'Sin cables' }
];
export default function DetalleProducto() {
const { id } = useParams();
// ⚠️ IMPORTANTE: useParams devuelve SIEMPRE strings.
// Si vuestros IDs son numéricos, debéis convertirlo.
const producto = baseDeDatos.find(item => item.id === Number(id));
// Manejo de caso "No encontrado"
if (!producto) {
return <h2>Producto no encontrado 😢</h2>;
}
return (
<div className="detalle">
<h1>{producto.nombre}</h1>
<p>Precio: {producto.precio}€</p>
<p>Descripción: {producto.desc}</p>
<Link to="/productos">⬅ Volver al listado</Link>
</div>
);
}
Los parámetros de URL son siempre string. Aunque la URL sea /productos/5, useParams devolverá { id: "5" }.
Si hacéis item.id === id (comparación estricta con número), fallará. Usad Number(id) o parseInt(id).
