prerender por página: el híbrido fino
La exportación `export const prerender` es el interruptor más fino del renderizado: una constante a nivel de módulo que el compilador lee estáticamente para elegir, ruta por ruta, si una página se hornea o se sirve viva. Cómo invertir el valor por defecto, cuándo prerenderizar, y qué información impide congelar una página.
La bandera que el compilador lee
prerender es una exportación a nivel de módulo: una constante que declaras en el frontmatter de la página, fuera de cualquier función. Su valor decide si esa ruta se calcula en el build (true) o en la petición (false). Lo esencial es quién la lee y cuándo: la lee el compilador, en tiempo de build, antes de que exista petición alguna.
---// src/pages/precios.astroexport const prerender = true; // esta ruta se hornea, pase lo que pase con outputconst planes = await cargarPlanes();---<h1>Precios</h1>De ahí sale una regla que no conviene olvidar: el valor tiene que ser analizable estáticamente. Un literal true o false, no una expresión que dependa del entorno ni un cálculo en tiempo de ejecución. El compilador necesita conocer el momento de render antes de ejecutar nada, así que intentar decidir prerender a partir de una variable, una condición o una llamada no funciona: el momento de render no puede depender de datos que solo existen cuando la página ya corre. Es una restricción, sí, pero también una garantía —el modo de cada ruta queda fijado y es legible con solo mirar la primera línea del fichero.
Esa lectura estática tiene un beneficio que va más allá de tu comodidad. Como Astro sabe, antes de construir, exactamente qué rutas necesitan servidor y cuáles no, puede hornear las estáticas a ficheros y meter solo las dinámicas en el punto de entrada del adaptador. La bandera no es una sugerencia que se evalúa tarde: es un dato del build que decide qué código acaba en el servidor. Y funciona igual en los endpoints .ts, no solo en las páginas .astro.
// src/pages/manifiesto.json.ts -> horneado aunque el sitio sea 'server'export const prerender = true;export const GET = () => Response.json({ nombre: 'Mi sitio', version: 7 });Un endpoint de datos que no cambia entre despliegues no tiene por qué costar una invocación de servidor en cada llamada: prerenderízalo y se convierte en un fichero servido por la CDN, aunque el resto del proyecto corra en modo server. La bandera decide el momento de cálculo de cualquier ruta —página o endpoint— con la misma regla.
Invertir el valor por defecto por ruta
La bandera cobra sentido cuando contradice al proyecto. Bajo output: 'static', el valor por defecto de cada página es prerender = true, así que escribir export const prerender = false es lo interesante: saca esa ruta concreta del horno y la vuelve bajo demanda. Bajo output: 'server' ocurre lo simétrico: el defecto es false, y escribir = true congela una ruta que, si no, se serviría viva.
---// Proyecto estatico (output: static) con UNA ruta dinamica:// src/pages/buscar.astroexport const prerender = false;const q = Astro.url.searchParams.get('q') ?? '';const resultados = await buscar(q);---<h1>Resultados para {q}</h1>Piensa en la bandera como el lápiz con el que trazas una costura a través de tu propio sitio. A un lado quedan las páginas horneadas —el documento, lo que es igual para todos—; al otro, las servidas vivas —la aplicación, lo que depende de quién pregunta y cuándo—. Esa costura no la dibuja el framework: la dibujas tú, ruta por ruta, y su forma es una de las decisiones de arquitectura más consecuentes que tomarás, porque determina qué parte de tu sitio necesita un servidor y qué parte vive feliz como ficheros planos.
Conviene saber qué no cambia al accionar el interruptor. El middleware, si lo tienes, sigue corriendo para las rutas dinámicas; las colecciones de contenido se consultan igual con getCollection en uno y otro momento; y una ruta dinámica con parámetros deja de necesitar getStaticPaths, porque el servidor resuelve el segmento al vuelo en vez de enumerarlo en el build. La bandera cambia cuándo corre la página, no el vocabulario con que la escribes.
Cuándo prerenderizar y cuándo no
La pregunta operativa es directa: ¿esta página es la misma para todo el mundo y estable entre publicaciones? Si la respuesta es sí, prerenderízala; si es no, sírvela bajo demanda. De ahí salen dos listas casi mecánicas.
- Hornéala cuando su contenido se conoce en el build, es idéntico para cada visitante, cambia con poca frecuencia y su conjunto de rutas es acotado: una portada, un artículo de blog, una página de documentación, una landing.
- Sírvela viva cuando depende de la petición o del usuario, debe estar siempre fresca, o su universo de rutas es ilimitado: un buscador, un panel de cuenta, un carrito, un catálogo de millones de fichas.
Ese último criterio —el tamaño del universo de rutas— decide muchos casos por sí solo. Prerenderizar exige enumerar de antemano cada página que existirá, y hay conjuntos que no caben en esa foto: los resultados de una búsqueda son infinitos, las fichas de un catálogo gigantesco tardarían horas en hornearse, un perfil por usuario nace después del despliegue. Cuando el conjunto es abierto o desmesurado, el bajo demanda no es una preferencia sino la única vía practicable, porque el build no puede listar lo que no cabe o aún no existe.
El límite: lo que no se puede hornear
Muchas veces la elección no es tuya: la naturaleza de la página la decide. Una ruta que lee algo que solo existe en la petición —las cabeceras que envió el navegador, una cookie de sesión, la cadena de búsqueda de la URL, la dirección IP del visitante— no puede prerenderizarse de forma sensata, porque en el build no hay petición de la que leer esos datos.
---// Esto NO puede ser estatico: depende de la peticionexport const prerender = false; // obligatorio aquiconst token = Astro.cookies.get('sesion')?.value;const idioma = Astro.request.headers.get('accept-language');---Si por descuido dejas una página así como estática, no obtienes un error claro sino algo peor: valores vacíos o congelados. La cookie sale indefinida, la cabecera nula, la búsqueda en blanco, y algunos accesos —como la IP del cliente— lanzan directamente. El síntoma es una página que “a veces no personaliza”, un fallo escurridizo cuyo origen es haber pedido a un fichero horneado que se comporte como un servidor vivo. Por eso el límite es útil: no es una molestia, es la guía que decide por ti muchos casos dudosos. Si la página necesita la petición, es bajo demanda, y no hay más que hablar.
El razonamiento inverso también ilumina. Si repasas una página y ninguna de sus líneas toca la petición —ni cookies, ni cabeceras, ni la consulta, ni la IP—, entonces nada impide hornearla, y probablemente deberías. La ausencia de accesos de petición es la firma de un documento; su presencia, la de una aplicación. Leer una página con esa pregunta en la cabeza —¿toca esto la petición?— convierte la decisión de prerender en algo casi mecánico.
En la práctica, ese hábito de lectura vuelve la costura casi automática: recorres tus páginas, señalas cuáles tocan la petición, y esas —y solo esas— llevan la bandera dinámica. El resto se queda en el valor por defecto estático, que es donde quieres que viva la mayoría. Así, prerender deja de ser una decisión que tomas página a página con esfuerzo y se vuelve la consecuencia de una única lectura atenta del código.
Un checklist rápido para decidir de un vistazo:
- ¿Lee cookies, cabeceras, la consulta o la IP? Entonces dinámica, sin excepción.
- ¿Su universo de rutas es ilimitado o crece sin control? Dinámica.
- ¿Debe reflejar cambios al segundo, sin margen de retraso? Dinámica.
- ¿Nada de lo anterior? Estática, y disfrutas de su velocidad y su baratura.