react-router-rutas-dinamicas

Rutas dinámicas en React Router

  • 4 min

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>
  );
}
Copied!

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>
  );
}
Copied!

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>
  );
}
Copied!

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>
  );
}
Copied!

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).