wandres.dev
RUTAS DINÁMICAS · params y getStaticPaths

Pasar datos con props desde getStaticPaths

La segunda clave de cada ruta: mientras params decide la URL, props adjunta los datos ya listos para esa página, que se leen en Astro.props sin repetir la búsqueda. El patrón canónico de una página por entrada de una colección, y por qué params identifica mientras props hidrata.

⏱ 15 min

getStaticPaths no solo decide qué URLs existen: también puede entregar, junto a cada ruta, los datos que esa página necesitará. Cada objeto del array admite una segunda clave, props, que viaja emparejada con params. Si params responde a qué dirección genero, props responde a con qué datos la relleno. La consecuencia es limpia y potente: la página recibe su contenido ya masticado en Astro.props, sin tener que volver a buscarlo. Sobre esa pareja se levanta el patrón más común de todo Astro: una página por cada entrada de una colección.

🎯 Al terminar esta lección sabrás
  • Adjuntar una clave props a cada objeto devuelto por getStaticPaths.
  • Leer esos datos en el cuerpo de la página con Astro.props.
  • Distinguir el papel de params —identificar la ruta— del de props —alimentar la página—.
  • Aplicar el patrón de generar una página por cada entrada de una colección.

params identifica, props acompaña

La tentación, al programar una ruta dinámica, es usar el parámetro para volver a buscar el dato dentro de la página: recibo slug, y con él consulto de nuevo la colección para encontrar el post. Funciona, pero es trabajo repetido. getStaticPaths ya tenía delante la colección entera cuando decidió las rutas; obligar a la página a redescubrir lo que aquella ya sabía es dar dos vueltas al mismo dato.

La clave props elimina ese rodeo. En el mismo objeto donde declaras params, adjuntas los datos que la página va a necesitar, y estos llegan intactos a Astro.props.

---
// src/pages/blog/[slug].astro
export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({
    params: { slug: post.id },   // decide la URL
    props: { post },             // acompana el dato
  }));
}

const { post } = Astro.props;    // llega ya listo
---
<h1>{post.data.title}</h1>

La división de trabajo es nítida y merece grabarse. params es lo que se convierte en URL: por fuerza son cadenas, porque una dirección es texto. props es lo que se convierte en contenido de la página: puede ser un objeto rico —la entrada entera, con su data tipado— porque nunca toca la URL. Uno mira hacia fuera, hacia el visitante que teclea la dirección; el otro mira hacia dentro, hacia la plantilla que se va a renderizar.

ℹ️
El dato se calcula una vez, en el sitio que ya lo tenía

El valor de props no es solo comodidad, es evitar duplicar una búsqueda. getStaticPaths recorre la colección para saber qué rutas existen; ya tiene cada entrada en la mano en ese preciso momento. Pasarla por props aprovecha ese hallazgo en lugar de tirarlo y repetirlo en cada página. Sin props, tendrías el slug y un segundo getEntry o un find dentro del cuerpo para recuperar lo que la función ya había tocado. Con props, el dato se busca una vez, donde naturalmente aparece, y se entrega a su destino.

El patrón: una página por entrada de la colección

Juntando params y props nace el gesto que define el enrutado orientado a datos: recorrer una colección con map y emitir, por cada entrada, un objeto que usa su identificador como params y la entrada completa como props. Un post de la colección, una ruta materializada; añade un post, aparece su página; borra otro, desaparece la suya.

---
import { getCollection, render } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({
    params: { slug: post.id },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await render(post);
---
<article>
  <h1>{post.data.title}</h1>
  <Content />
</article>

Aquí conviven las dos mitades del framework: los datos deciden cuántas páginas hay —una por entrada— y el fichero [slug].astro decide cómo es cada una. El número de páginas ya no lo escribes tú; lo dicta el tamaño de la colección. Es la diferencia entre mantener a mano un fichero por artículo y tener un único fichero que se multiplica solo según el contenido.

flowchart TD
COL[coleccion de posts] --> GSP[getStaticPaths recorre con map]
GSP --> O1[objeto params slug mas props post]
GSP --> O2[objeto params slug mas props post]
O1 --> URL1[params define la url]
O1 --> PG1[props llena Astro props]
O2 --> URL2[params define la url]
O2 --> PG2[props llena Astro props]
PG1 --> R[la pagina renderiza sin volver a consultar]
PG2 --> R
style COL fill:#89b4fa,color:#11111b
style R fill:#a6e3a1,color:#11111b

Observa un matiz de perspectiva. getStaticPaths ve la colección entera: es una vista global, capaz de razonar sobre el conjunto. Cada página, en cambio, solo ve su rebanada: lo que recibe por props. Esa asimetría no es una limitación, es una herramienta. Como la función tiene delante todos los datos a la vez, puede calcular cosas que una página aislada no podría —y pasárselas ya resueltas—.

Enriquecer con datos derivados: vecinos y cálculo global

El caso que mejor muestra el poder de props es el de los datos que solo existen en relación con el conjunto: el post anterior y el siguiente, la posición en una serie, los artículos relacionados. Una página por sí sola no sabe quiénes son sus vecinos; pero getStaticPaths, que ordena la colección completa, sí lo sabe, y puede adjuntar esa información a cada ruta.

---
export async function getStaticPaths() {
  const posts = (await getCollection('blog')).sort(
    (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
  );
  return posts.map((post, i) => ({
    params: { slug: post.id },
    props: {
      post,
      anterior: posts[i + 1],   // el conjunto conoce a los vecinos
      siguiente: posts[i - 1],
    },
  }));
}
---

La página recibe por props no solo su propia entrada, sino su contexto dentro de la colección, calculado una sola vez y ya listo para pintar unos enlaces de navegación. Reproducir esto desde dentro de cada página obligaría a recargar y reordenar la colección entera en cada una —el mismo trabajo global repetido mil veces— para al final quedarte con dos vecinos. props te deja hacer ese cálculo global donde tiene sentido, una vez, y repartir el resultado.

🆔

params para la URL

Solo cadenas, porque forman la direccion. Identifican la ruta y la distinguen de sus hermanas.

📦

props para la página

Objetos ricos, la entrada entera si hace falta. Nunca tocan la URL; llenan Astro props.

🔁

Sin segunda búsqueda

El dato que getStaticPaths ya tenia viaja con la ruta. La pagina no vuelve a consultar la coleccion.

🌐

Vista global, rebanada local

La funcion ve el conjunto y calcula vecinos o posicion. Cada pagina recibe solo su parte, ya resuelta.

💡
Si la página lo necesita para pintarse, pásalo por props

Una regla práctica para decidir qué va en props: todo lo que la página necesite para renderizarse y que dependa de cuál entrada es. El cuerpo del post, su título, sus vecinos, sus etiquetas relacionadas. Lo que sea igual para todas las páginas —un pie común, una constante— no gana nada viajando por props y puede vivir en el propio componente. props es para lo que varía de ruta en ruta y que la función, con su vista de conjunto, está en mejor posición de calcular.

📝
En estático, props se resuelve en el build

Como getStaticPaths corre durante la compilación, lo que pasas por props se calcula también entonces y queda incrustado en el HTML generado. No hay coste en el navegador ni consulta en vivo: el visitante recibe una página en la que los datos ya están horneados. Por eso puedes pasar objetos elaborados sin miedo a un sobrecoste en tiempo de ejecución; ese tiempo, sencillamente, no existe en un sitio estático.

params y props reparten la ruta en identidad y estado

La pareja params y props codifica una de las distinciones más viejas y fértiles del diseño de software: separar qué es una cosa de qué contiene. params es identidad —lo mínimo que distingue esta ruta de todas las demás, lo que va en la URL porque es su nombre público y único—. props es estado —todo lo que hace falta para dar cuerpo a esa identidad, que no cabe ni debe caber en la dirección—. Es la misma línea que separa una clave primaria de las columnas de su fila, el identificador de un objeto de sus atributos, la dirección de un recurso de su representación. Y no es una separación gratuita: nace de que ambas cosas tienen naturalezas y públicos distintos. La identidad es texto, es estable, es visible, la teclea un humano y la guarda un enlace; el estado es estructurado, es rico, es interno, lo consume una plantilla. Confundirlos —meter el contenido en la URL, o pretender identificar por el estado— produce sistemas frágiles: URLs monstruosas o entidades sin nombre estable. Astro te obliga con suavidad a hacer bien esa distinción cada vez que generas una ruta: di quién es esta página en params, di qué muestra en props. Y una vez separadas, cada una se optimiza a su modo. La identidad viaja ligera y forma direcciones limpias; el estado se calcula una sola vez, con la colección entera a la vista, y se reparte ya resuelto. Interiorizar que una ruta es siempre un par identidad-estado, y no un amasijo indiferenciado de datos, es lo que te permite razonar sobre navegación, sobre cachés, sobre enlaces permanentes, mucho más allá de este framework.

⚔️ Identidad y estado en cada ruta
  1. En src/pages/blog/[slug].astro, pasa la entrada completa por props y renderízala leyendo Astro.props sin ningún getEntry adicional.
  2. Compara con una versión que solo reciba el slug y vuelva a buscar el post dentro de la página; razona qué trabajo duplicas.
  3. Ordena la colección en getStaticPaths y adjunta por props el post anterior y el siguiente; pinta unos enlaces de navegación.
  4. Explica por qué esos vecinos no podrían calcularse cómodamente desde dentro de una sola página, y sí desde la vista global de la función.