getStaticPaths en modo estático
La función que cierra el patrón: en un sitio estático, un fichero dinámico exporta getStaticPaths para enumerar en el build todas las rutas que existen. Cómo devuelve un array de objetos con params, cómo cada objeto se materializa en un HTML, y por qué lo no enumerado es, por definición, un 404.
Un fichero con corchetes describe un patrón, pero un sitio estático no puede hornear infinitas páginas para un patrón infinito: necesita una lista finita de casos reales. getStaticPaths es esa lista hecha función. La exportas desde el fichero dinámico, se ejecuta una vez durante el build, y devuelve un array donde cada elemento declara una ruta concreta que quieres materializar. Es el puente que conecta la forma de las URLs —que dan los corchetes— con su contenido real —que dan tus datos—.
- Exportar
getStaticPathsdesde un fichero de ruta dinámica. - Devolver un array de objetos, cada uno con su clave
params. - Hacer que las claves de
paramscoincidan con los nombres de los corchetes. - Comprender que el build materializa un HTML por objeto y trata el resto como 404.
La función que enumera las rutas
En modo estático, un fichero dinámico está incompleto sin getStaticPaths. Es una función que exportas —convencionalmente async, aunque puede ser síncrona— y que Astro busca por nombre en cada fichero con corchetes. Su cometido es único y claro: devolver la lista de todas las rutas que ese patrón debe producir.
---
// src/pages/blog/[slug].astro
export async function getStaticPaths() {
return [
{ params: { slug: 'hola-mundo' } },
{ params: { slug: 'segundo-post' } },
{ params: { slug: 'tercero' } },
];
}
const { slug } = Astro.params;
---
<h1>Post: {slug}</h1>
Esas tres entradas le dicen a Astro: de todo el universo imaginable de valores para slug, materializa exactamente estos tres. El build generará /blog/hola-mundo, /blog/segundo-post y /blog/tercero, y ninguna otra variante. El patrón describía infinitas posibilidades; getStaticPaths selecciona el subconjunto que de verdad existe.
En la práctica, ese array casi nunca se escribe a mano. Lo habitual es derivarlo de una fuente de datos —una colección de contenido, un JSON, una API consultada en el build— y transformarla con un map en la forma que getStaticPaths espera. La función es el punto donde tus datos se convierten en rutas.
---
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.id },
}));
}
---
Dos matices sobre su firma. La función puede ser async o síncrona: la haces async cuando dentro esperas datos —una colección, un fetch—, y la dejas síncrona si el array es puro cálculo local. Y devolver un array vacío es legítimo: significa que el patrón, de momento, no genera ninguna página, sin que el build proteste. Cero rutas es un resultado válido, no un fallo; simplemente no se hornea nada para ese fichero.
params: la clave que casa con los corchetes
Cada objeto del array lleva una clave obligatoria: params. Y params es a su vez un objeto cuyas claves deben coincidir, exactamente, con los nombres que pusiste entre corchetes en el fichero. Si el archivo es [slug].astro, cada params lleva una clave slug; si es [categoria]/[id].astro, cada params lleva categoria e id. Esa coincidencia no es decorativa: es el contrato que Astro verifica.
---
// src/pages/tienda/[categoria]/[id].astro
export async function getStaticPaths() {
return [
{ params: { categoria: 'ropa', id: '1' } },
{ params: { categoria: 'ropa', id: '2' } },
{ params: { categoria: 'libros', id: '1' } },
];
}
---
Cada objeto describe una combinación completa de todos los parámetros del fichero. Los valores deben ser cadenas —o números, que Astro convierte a cadena para formar la URL—; no puedes meter un objeto ni un array donde va un segmento, porque un segmento de URL es, al final, texto. El valor que declaras aquí es exactamente el que después leerás en Astro.params dentro de esa página.
Si el fichero es [slug].astro pero tu params lleva una clave id, Astro no genera nada útil: la ruta no casa y el build se queja. El error suele ser un despiste al renombrar —cambias el corchete y olvidas el params, o al revés—. Ante una ruta dinámica que no aparece, la primera sospecha debe ser siempre esta: comprobar que el nombre del corchete y la clave de params son idénticos, carácter a carácter.
Se ejecuta en el build y define un universo cerrado
El cuándo de getStaticPaths es tan importante como el qué. En modo estático, la función corre una sola vez, durante la compilación, no en cada visita. Astro la llama, recibe el array, y por cada objeto genera un fichero HTML completo que se despliega como archivo plano. Cuando un visitante pide /blog/hola-mundo, no se ejecuta ninguna lógica: se sirve un HTML que ya existía. getStaticPaths es trabajo de fábrica, no de tienda.
flowchart TD D[fichero blog slug] --> G[getStaticPaths en el build] G --> R[array de objetos con params] R --> H1[genera blog hola punto html] R --> H2[genera blog adios punto html] R --> H3[genera blog otro punto html] X[peticion a blog inexistente] --> NF[404 no fue enumerada] style D fill:#89b4fa,color:#11111b style H1 fill:#a6e3a1,color:#11111b style H2 fill:#a6e3a1,color:#11111b style H3 fill:#a6e3a1,color:#11111b style NF fill:#f38ba8,color:#11111b
De esto se sigue una propiedad que conviene entender bien: el conjunto de rutas es cerrado. Solo existen las URLs que getStaticPaths enumeró; cualquier otra, aunque encaje en el patrón, devuelve un 404. Pedir /blog/un-slug-que-nunca-declaraste no ejecuta el fichero con ese valor: simplemente no hay página, porque no se horneó. En estático no hay resolución en vivo, y por eso lo no previsto en el build no existe en producción.
Ese universo cerrado es, en realidad, una garantía valiosa. Como todas las rutas se conocen en la compilación, un enlace roto puede detectarse antes de desplegar, el sitemap es exhaustivo por construcción y no hay forma de que un valor inesperado dispare un error en tiempo de ejecución: no hay tiempo de ejecución. Pagas la rigidez de tener que enumerarlo todo, y a cambio recibes previsibilidad total sobre qué páginas existen.
Enumerar, no calcular
getStaticPaths lista las rutas reales. El patron describe la forma; la funcion elige que casos de esa forma existen.
params casa con corchetes
Cada clave de params coincide, letra por letra, con un nombre entre corchetes del fichero. Ese es el contrato.
Corre en el build
Se ejecuta una vez al compilar. Cada objeto devuelto se hornea en un HTML que se sirve como fichero plano.
Universo cerrado
Solo existe lo enumerado. Cualquier otra URL que encaje en el patron es 404, porque no hay resolucion en vivo.
La forma sana de pensar getStaticPaths es de dentro hacia fuera: primero consigue tus datos —la colección, el JSON, la respuesta de la API—, y solo entonces mapéalos a la forma params. Así el número de páginas lo deciden los datos, no una lista escrita a mano que se desincroniza. Añadir un post a la colección hace aparecer su ruta sin tocar el fichero; borrarlo la hace desaparecer. La ruta se vuelve una consecuencia automática del contenido, que es justo lo que quieres.
Cada objeto de getStaticPaths admite, junto a params, una segunda clave opcional: props. Mientras params decide qué URL se genera, props te deja adjuntar datos ya listos para esa página, de modo que no tengas que volver a buscarlos en el cuerpo del componente. Aquí basta con saber que existe y que viaja emparejada con cada ruta; su patrón completo —y por qué evita una segunda consulta— es el tema de una lección posterior de este mismo nivel.
Merece una última mirada el papel de getStaticPaths como frontera temporal. Todo lo que ocurre dentro de ella pertenece al build: las consultas, los filtros, los cálculos que deciden las rutas. Todo lo que ocurre fuera, en el cuerpo del componente, se ejecuta por cada página ya materializada. Esa línea separa el trabajo que se hace una vez para todo el sitio del que se repite ruta por ruta, y ubicar cada cosa en su lado correcto es media eficiencia del build ganada de antemano.
En la lección anterior vimos que un fichero con corchetes es una función: una regla que, dado un valor, produce una página. getStaticPaths es lo que ocurre cuando decides precomputar esa función para todos los argumentos que te importan y guardar el resultado. En informática esto tiene nombre y una larga tradición: es memoización, es una lookup table, es cambiar cálculo por almacenamiento. En vez de resolver la ruta cada vez que alguien la pide, resuelves todas las rutas una vez y sirves respuestas ya cocinadas. El eje que estás moviendo es el clásico espacio contra tiempo: gastas espacio en disco —mil ficheros HTML— para no gastar tiempo de cómputo en cada visita. Y como toda tabla precalculada, hereda sus dos verdades. La primera es la fortaleza: si está en la tabla, la respuesta es instantánea y no puede fallar, porque ya se calculó y se revisó en un entorno controlado. La segunda es la frontera: si no está en la tabla, sencillamente no existe —de ahí el 404 para lo no enumerado—, porque una tabla no extrapola, solo consulta. Esta es la diferencia esencial entre generación estática y renderizado en servidor, y no es un detalle de implementación sino una elección sobre cuándo pagar el trabajo: por adelantado y para siempre, o bajo demanda y cada vez. getStaticPaths te obliga a hacer esa elección explícita, enumerando en el build el dominio entero de tu función de rutas. Cuando entiendes que estás construyendo una tabla y no escribiendo un manejador, dejas de preguntarte por qué hay que listar las páginas: listar es, precisamente, lo que significa precomputar.
- Crea
src/pages/blog/[slug].astrocon ungetStaticPathsque devuelva a mano tres objetos conparams; visita las tres URLs. - Pide una cuarta URL que no enumeraste y confirma que devuelve 404; explica por qué el patrón no basta para servirla.
- Sustituye el array escrito a mano por un
mapsobregetCollection('blog')y comprueba que las rutas ahora las deciden los datos. - Renombra el corchete a
[id]sin tocarparamsy observa el error del build; corrige la clave y razona qué contrato se rompió.