Saltar al contenido

Los modos de render en Astro 7

Renderizar es convertir un componente en HTML; la única pregunta es cuándo ocurre. Astro 7 no ofrece tres modos rivales sino un eje —build o petición— con un valor por defecto que fija `output` y un interruptor por página. Estático por defecto, servidor bajo demanda, qué produce cada build y por qué `output: 'hybrid'` dejó de existir.

Un solo eje: build o petición

Conviene desmontar cuanto antes la idea de que SSG, SSR e híbrido son tres tecnologías distintas. Renderizar un componente .astro es siempre el mismo cálculo: tomas datos, evalúas el frontmatter, produces marcado. La distinción que importa no está en cómo se renderiza, sino en cuándo se ejecuta ese cálculo y, por tanto, quién carga con su coste y qué información tiene delante.

Hay exactamente dos momentos posibles. Uno es el build: el cálculo corre una sola vez cuando publicas, su resultado se guarda como un fichero HTML plano y ese fichero se sirve tal cual a cada visita. Es lo que se llama estático o SSG. El otro es la petición: el cálculo corre en un servidor cada vez que alguien pide la página, con los datos frescos de ese momento y, si hace falta, particularizado para ese visitante. Es lo que se llama bajo demanda o SSR.

Todo lo demás de este nivel —el rendimiento, el coste, la frescura de los datos, qué puedes leer de la petición— se deduce de esa única elección temporal. Por eso la palabra modo engaña: no eliges una maquinaria, eliges un momento. Y como el momento se decide para cada página por separado, lo que de verdad tienes no son tres cajas, sino un dial que cada ruta ajusta a su naturaleza.

Vale la pena insistir en que el código que escribes es casi el mismo en ambos momentos. El frontmatter de una página —sus import, su await, su lógica— corre igual se ejecute en el build o en la petición; lo que cambia no es lo que tecleas, sino cuándo se evalúa y con qué contexto disponible. Esa casi identidad es una buena noticia práctica: mover una página de un momento al otro rara vez exige reescribirla, solo cambiar una línea y revisar qué datos de petición asume. Aprender un momento es, en gran medida, aprender el otro.

Conviene grabar el contraste esencial, porque de él cuelga todo lo demás del nivel:

  • En el build: corre una vez, produce un fichero, sirve idéntico a todos, coste fijo, datos congelados hasta el próximo despliegue.
  • En la petición: corre por visita, produce una respuesta viva, particularizable por visitante, coste por uso, datos frescos del instante.

output: el valor por defecto, no una jaula

El fichero de configuración fija con output cuál de los dos momentos es el valor por defecto de todo el proyecto. Con output: 'static' —lo que trae un proyecto nuevo— cada página se hornea en el build salvo que digas lo contrario. Con output: 'server', cada página se renderiza bajo demanda salvo que la excluyas explícitamente.

ASTRO
---// astro.config.mjsimport { defineConfig } from 'astro/config';import node from '@astrojs/node';export default defineConfig({  output: 'static',            // por defecto: hornear en el build  // output: 'server',         // por defecto: renderizar por peticion  adapter: node({ mode: 'standalone' }),});

La palabra clave es por defecto. output no encierra tus páginas en un régimen único: solo declara qué momento se asume cuando una página no dice nada. Cada valor implica una postura sobre la mayoría de tus rutas:

  • output: 'static' asume que casi todo es contenido horneable y trata lo dinámico como excepción puntual.
  • output: 'server' asume que casi todo depende de la petición y trata lo estático como la excepción que se congela.

En cuanto una sola ruta necesita renderizarse bajo demanda —en un proyecto por lo demás estático— Astro exige un adaptador, el paquete que traduce tu sitio al runtime que lo ejecutará: Node, Vercel, Cloudflare, Netlify. Sin adaptador no hay servidor vivo que atienda esas páginas, y el build fallará avisándote. El adaptador es la frontera real entre los dos mundos: un sitio puramente estático no necesita ninguno, porque produce ficheros que cualquier CDN sirve; en el momento en que una ruta pasa a bajo demanda, tu despliegue deja de ser una carpeta de ficheros y pasa a incluir una función de servidor. Esa es la consecuencia arquitectónica de cruzar el eje, y conviene tenerla presente antes de accionar el interruptor.

Un ejemplo concreto de excepción: en un proyecto estático, una sola página que muestra un perfil por sesión se saca del horno con una línea, y el resto del sitio sigue horneándose sin enterarse.

ASTRO
---// src/pages/perfil.astro  (proyecto con output: 'static')export const prerender = false;   // solo ESTA ruta se sirve vivaconst usuario = await usuarioActual(Astro.cookies);---<h1>Perfil de {usuario.nombre}</h1>

Qué produce cada build

La diferencia entre los dos momentos se palpa en lo que el build deja en disco. Un proyecto enteramente estático produce una carpeta de ficheros —un HTML por ruta, más los assets— y nada más: no hay proceso que arrancar, solo contenido que subir a cualquier alojamiento.

Shell
# output: 'static' sin rutas dinamicas -> solo ficherosdist/  index.html  blog/primer-post/index.html  _astro/          # css y js de las islas

En cuanto una ruta pasa a bajo demanda, el build emite además un punto de entrada de servidor —una función que el adaptador sabe ejecutar en su runtime— junto a los ficheros de las rutas que sí se hornearon.

Shell
# una ruta prerender=false, u output: 'server' -> aparece un servidordist/  client/          # lo estatico y los assets  server/          # la funcion que atiende lo dinamico

Mirar el dist/ es la forma más honesta de saber qué has construido de verdad. Si solo hay HTML, tienes un sitio que cualquiera aloja gratis en un bucket. Si aparece una carpeta server/, has adquirido una dependencia operativa —algo que ejecutar, vigilar y pagar— y conviene que sea porque alguna ruta lo necesitaba, no por descuido.

El fin del modo híbrido

Quien venga de versiones antiguas recordará un tercer valor, output: 'hybrid', para sitios mayormente estáticos con algún trozo dinámico. Se retiró, y su desaparición es una lección de diseño. Resultó que static ya era híbrido por naturaleza —estático por defecto, con rutas puntuales marcadas como dinámicas— así que la tercera palabra no añadía ningún poder: solo duplicaba, con otro nombre, lo que la bandera por página ya permitía.

Que una opción desaparezca y el sistema no pierda expresividad es raro y revelador. No se esfumó ninguna capacidad: static con prerender hace todo lo que hybrid hacía, y además con un concepto menos que aprender. Cuando un rediseño elimina vocabulario sin quitar poder, suele ser señal de que se ha encontrado el modelo correcto —el que describe el problema con las menos piezas posibles.

Así que el mapa completo es sencillo. La combinación por defecto de static es “casi todo horneado, alguna ruta viva”; la de server es “casi todo vivo, alguna ruta congelada”. Ambas recorren el mismo eje desde extremos opuestos, y ambas admiten mezcla. Elegir entre las dos no es elegir capacidades —las capacidades son idénticas— sino elegir cuál de los dos regímenes describe mejor la mayoría de tus páginas, para escribir la mínima cantidad de excepciones.