Parámetros en React Router con useParams

Introducción al problema

Hasta ahora tenemos el listado de ofertas funcionando con React Router, pero hay algo clave que falta: no podemos entrar al detalle de una oferta concreta.

Ahora mismo:

  • ✅ Vemos un listado de trabajos
  • ✅ Podemos navegar por algunas rutas
  • Pero no existe una página de detalle para cada oferta

Eso significa que aunque tengamos el id de cada trabajo, no lo estamos usando en la URL para cargar la información específica de esa oferta.

En esta clase vamos a:

  • Crear una página de detalle para una oferta
  • Definir una ruta dinámica que reciba un parámetro (la id)
  • Usar el hook useParams de React Router para leer esa id desde la URL

Qué son los parámetros de ruta

En una URL como:

/jobs/1234
  • /jobs es la ruta base
  • 1234 es un parámetro dinámico: la id de la oferta

React Router nos permite definir rutas que no son fijas, sino que incluyen segmentos variables que luego podemos leer:

/jobs/:id
  • :id es un placeholder
  • Cuando naveguemos a /jobs/abc, el parámetro id será "abc"
  • Cuando naveguemos a /jobs/42, el parámetro id será "42"

El objetivo es capturar ese valor para poder:

  • Pedir los detalles a la API
  • Mostrar el contenido específico de esa oferta

Creando la página de detalle

Primero creamos un nuevo componente para el detalle de la oferta.

// src/pages/JobDetail.jsx

export function JobDetail() {
  return <h1>Job detail</h1>
}

De momento solo muestra un texto genérico.

Luego lo iremos mejorando con la id y, más adelante, con los datos reales.

Definiendo la ruta dinámica con :id

Ahora vamos a crear una ruta que reciba una id dinámica.

En el archivo donde definimos las rutas (por ejemplo App.jsx o router.jsx):

import { Route, Routes } from 'react-router'
import { JobDetail } from './pages/JobDetail'

export function App() {
  return (
    <Routes>
      {/* Otras rutas */}
      <Route path="/jobs/:id" element={<JobDetail />} />
    </Routes>
  )
}

Puntos clave

  • path="/jobs/:id"
    • :id indica que esa parte de la URL es un parámetro dinámico
  • element={<JobDetail />}
    • Cuando la URL coincida con /jobs/algo, se renderiza JobDetail

Prueba en el navegador

  • Ve a /jobs/1
  • Ve a /jobs/jajajata
  • Ve a /jobs/1234

En todos los casos se renderiza la misma página, pero con diferentes valores de id en la URL.

Leyendo los parámetros con useParams

Ahora queremos que JobDetail pueda leer la id de la URL.

React Router nos da el hook useParams para esto:

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

export function JobDetail() {
  const params = useParams()
  console.log(params)

  return (
    <>
      <h1>Job detail</h1>
      <pre>{JSON.stringify(params, null, 2)}</pre>
    </>
  )
}

Si visitas /jobs/jajajata, verás algo así en la consola:

{ "id": "jajajata" }

Y si visitas /jobs/1234:

{ "id": "1234" }

Desestructurando el parámetro

Normalmente desestructuramos directamente el parámetro:

import { useParams } from 'react-router'

export function JobDetail() {
  const { id } = useParams()

  return (
    <>
      <h1>Job detail</h1>
      <h2>La ID es: {id}</h2>
    </>
  )
}

Puntos importantes

  • useParams() devuelve un objeto
  • Cada clave del objeto corresponde al nombre del parámetro en la ruta
  • Todos los valores son strings, aunque parezcan números

El nombre del parámetro manda

Un error muy típico es pensar que el parámetro siempre se llama id.

En realidad, el nombre depende de lo que tú pongas en la ruta.

Si defines la ruta así:

<Route path="/jobs/:jobId" element={<JobDetail />} />

Entonces useParams() devolverá:

{ "jobId": "1234" }

Y en el componente tendrás que hacer:

export function JobDetail() {
  const { jobId } = useParams()

  return (
    <>
      <h1>Job detail</h1>
      <h2>La ID es: {jobId}</h2>
    </>
  )
}

Resumen

  • Si la ruta es "/jobs/:id"useParams() devuelve { id }
  • Si la ruta es "/jobs/:jobId"useParams() devuelve { jobId }

El nombre del parámetro tiene que coincidir exactamente entre:

  1. Lo que pones en el path (:id, :jobId, :slug, etc.)
  2. Lo que desestructuras desde useParams()

Usando la ID para pedir datos a la API

Aunque en un principio solo mostramos la id en pantalla, el siguiente paso natural es usarla para pedir datos reales.

Por ejemplo:

import { useEffect, useState } from 'react'
import { useParams } from 'react-router'

export function JobDetail() {
  const { id } = useParams()
  const [job, setJob] = useState(null)

  useEffect(() => {
    if (!id) return

    fetch(`/api/jobs/${id}`)
      .then((res) => res.json())
      .then((data) => {
        setJob(data)
      })
      .catch((error) => {
        console.error('Error cargando el job', error)
      })
  }, [id])

  if (!job) {
    return <p>Cargando oferta...</p>
  }

  return (
    <section>
      <h1>{job.title}</h1>
      <p>{job.description}</p>
      <p>Ubicación: {job.location}</p>
    </section>
  )
}

Conceptos clave

  • La id viene de la URL, no de props: Esto hace que el componente sea independiente
  • Si el usuario recarga la página, la id sigue estando en la URL: La información no se pierde
  • Esto hace que la app sea más compartible y navegable (deep linking): Puedes copiar y pegar la URL

Por qué esto es importante

Imagina que encuentras una oferta que te interesa. Si la URL es /jobs/1234, puedes:

  • Copiarla y enviarla por WhatsApp
  • Guardarla en favoritos
  • Recargar la página sin perder contexto

Si la información estuviera solo en el estado de React (sin estar en la URL), al recargar perderías todo.

Múltiples parámetros en la URL

React Router también permite tener más de un parámetro en la ruta.

Por ejemplo, una ruta con id y página:

<Route path="/jobs/:id/page/:page" element={<JobDetail />} />

Y podrías leerlos así:

import { useParams } from 'react-router'

export function JobDetail() {
  const { id, page } = useParams()

  return (
    <>
      <h1>Job detail</h1>
      <p>La ID es: {id}</p>
      <p>Página: {page}</p>
    </>
  )
}

Ejemplo con URL /jobs/1234/page/2

{
  "id": "1234",
  "page": "2"
}

Otro ejemplo: keyword en la ruta

En lugar de usar query strings (?keyword=frontend), puedes usar parámetros de ruta:

<Route path="/jobs/keyword/:keyword" element={<JobsSearch />} />

Y en el componente:

export function JobsSearch() {
  const { keyword } = useParams()

  return (
    <>
      <h1>Resultados de búsqueda</h1>
      <p>Buscando ofertas para: {keyword}</p>
    </>
  )
}

Esto te permite construir URLs como:

  • /jobs/keyword/frontend
  • /jobs/keyword/react
  • /jobs/keyword/javascript

¿Cuándo usar parámetros vs query strings?

Usa parámetros de ruta cuando:

  • El valor identifica un recurso específico (id de producto, slug de artículo)
  • Es esencial para la página (sin él, la página no tiene sentido)
  • Quieres URLs más “limpias” y semánticas

Usa query strings cuando:

  • Son filtros opcionales (búsqueda, ordenamiento, página)
  • La página funciona sin ellos
  • Puedes tener muchos parámetros opcionales

Convirtiendo la id en número

Recuerda que useParams() siempre devuelve strings. Si necesitas un número:

export function JobDetail() {
  const { id } = useParams()
  const numericId = Number(id)

  // O usando parseInt
  const jobId = parseInt(id, 10)

  console.log(typeof id) // "string"
  console.log(typeof numericId) // "number"

  // ...
}

Esto es útil cuando necesitas hacer comparaciones numéricas o enviar la id a una API que espera números.

Validando parámetros

Es buena práctica validar que el parámetro tenga el formato esperado:

import { useParams, Navigate } from 'react-router'

export function JobDetail() {
  const { id } = useParams()

  // Si la id no es un número, redirigimos a 404
  if (isNaN(Number(id))) {
    return <Navigate to="/404" replace />
  }

  // Resto del componente...
}

O simplemente mostrar un mensaje de error:

export function JobDetail() {
  const { id } = useParams()

  if (!id || isNaN(Number(id))) {
    return (
      <div>
        <h1>Error</h1>
        <p>ID de oferta inválida</p>
      </div>
    )
  }

  // Resto del componente...
}

Resumen de la clase

En esta sesión hemos visto:

  • Por qué necesitamos rutas dinámicas para mostrar el detalle de una oferta
  • Cómo crear una página de detalle JobDetail
  • Cómo definir una ruta con parámetros usando path="/jobs/:id"
  • Cómo leer los parámetros de la URL con el hook useParams
  • Que el nombre del parámetro depende de lo que pongas en el path (:id, :jobId, :keyword, etc.)
  • Que podemos tener múltiples parámetros en una misma ruta
  • Cómo esa id nos sirve para pedir datos reales a una API
  • La diferencia entre parámetros de ruta y query strings
  • Cómo validar y convertir parámetros

Con esto ya tienes las bases para trabajar con URLs dinámicas en React Router y construir experiencias mucho más ricas: detalles de productos, perfiles de usuario, páginas de blog con slug, y mucho más.

Las URLs con parámetros hacen que tu aplicación sea más compartible, más navegable y más profesional. Son una pieza fundamental de cualquier aplicación web moderna.