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.
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].astroexport 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].astroexport 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.
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.
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.
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.