Diseñar el render por página: el mapa de decisión
El dial de renderizado no es un interruptor del proyecto sino una decisión por ruta, y un producto real usa las cuatro posiciones a la vez. Dos preguntas —cuán fresco debe ser el dato y si se personaliza por usuario— deciden cada página, y el mapa se aplica a un producto completo: e-commerce con blog, dashboard y documentación, con la elección de edge o región como último matiz.
El dial de renderizado que aprendiste —estático, SSR, server island— no es un interruptor que se acciona una vez para todo el proyecto: es una decisión que se toma ruta a ruta, y un producto real de verdad usa las cuatro posiciones a la vez. Diseñar el render es el arte de mirar cada página y preguntarle dos cosas: cuán fresco necesita ser su dato y si cambia según quién la pide. Las respuestas la colocan, sin ambigüedad, en el punto exacto del dial donde es más rápida y más barata sin dejar de ser correcta.
- Tratar el modo de render como decisión por ruta, no por proyecto.
- Decidir cada página con dos ejes: frescura del dato y personalización.
- Mapear un producto real completo a estático, SSR, server island y edge.
- Elegir entre edge y región como último matiz del renderizado dinámico.
Dos preguntas deciden el render de cada ruta
Toda la complejidad del renderizado se colapsa en dos preguntas, y su cruce forma un cuadrante que no deja huecos.
La primera: ¿el dato de esta página cambia entre el build y la petición? Si basta con lo que había al construir —un artículo, una página legal, la documentación—, es estático: se hornea una vez y lo sirve un CDN sin ejecutar nada. Si el dato debe estar fresco en el instante de la visita —un stock, un precio, un panel—, es dinámico y necesita resolverse bajo demanda.
La segunda: ¿la página es igual para todos o se personaliza por usuario? Una portada idéntica para el mundo entero es cacheable aunque su dato sea algo dinámico; un carrito o un dashboard dependen de quién pregunta y no se pueden compartir entre visitantes. El cruce de ambos ejes te dice si la ruta es estática, una server island cacheable, o SSR por usuario.
---
// El dial se acciona por pagina con una linea
export const prerender = true; // horneada en el build, servida por CDN
// export const prerender = false; // bajo demanda, resuelta en cada peticion
---
La posición por defecto sana es la más barata: estático. No vuelvas dinámica una página entera porque un dato pequeño sea fresco; extrae ese dato a una server island y deja el resto horneado. Escalar en el dial cuesta latencia y cómputo, así que sube solo el fragmento que de verdad lo necesita, no la página que lo contiene.
El cuadrante, sin huecos
Cruzar los dos ejes produce cuatro casillas, y toda página del mundo cae en una de ellas. Nombrarlas convierte la intuición en método.
Fijo y público
El dato basta con el build y es igual para todos. Estático puro, horneado y servido por el CDN. Un artículo, una landing, la documentación.
Fresco y público
El dato late pero es el mismo para todos, así que se cachea. Server island con su propio TTL sobre una página estática. El precio y el stock de una ficha.
Fresco y privado
El dato late y depende de quién pregunta, no se comparte. SSR bajo demanda por usuario. El carrito, el panel, el perfil.
Fijo pero contextual
El contenido es estable pero varía por idioma o región. Estático generado por variante, o una decisión ligera en el edge. Las rutas por locale.
El mapa aplicado a un producto real
Tomemos un producto completo —una tienda con blog, cuenta de usuario y documentación— y coloquemos cada zona en el dial. Este reparto es el patrón, no la excepción.
Blog y documentación
Estáticos puros. El dato solo cambia cuando publicas, así que se hornean en el build y se sirven desde el CDN con cero JS. El SEO y la velocidad son máximos.
Ficha de producto
Cáscara estática con el texto y las fotos, y una server island para el precio y el stock, que se resuelve aparte con su propia frescura sin recalcular la página entera.
Carrito y checkout
SSR bajo demanda. Dependen del usuario y de su sesión, no se comparten ni se cachean, y viven tras el adapter con la Sessions API.
Panel de cuenta
SSR tras un middleware de autenticación que resuelve la sesión en locals; dentro, islas para los widgets interactivos y actions para las mutaciones.
Fíjate en que el listado de productos es el caso más interesante: la rejilla, los filtros y el texto son estáticos y cacheables, pero el precio y la disponibilidad son server islands. Una página que parece dinámica es, en realidad, HTML estático servido al instante con unos pocos huecos que se rellenan con dato fresco. El patrón se lee limpio en el propio componente:
---
// src/pages/tienda/[slug].astro -> estatico por defecto
import Precio from '../../components/Precio.astro';
---
<h1>{producto.nombre}</h1>
<p>{producto.descripcion}</p>
<Precio server:defer>
<span slot="fallback">Consultando precio...</span>
</Precio>
La búsqueda, en cambio, sí es dinámica de arriba abajo: es un endpoint o una página SSR porque su resultado depende por completo de lo que el usuario teclea, y no hay build que pueda anticiparlo.
// src/pages/api/buscar.ts -> dinamico de arriba abajo, nada que hornear
export const prerender = false;
export async function GET({ url }) {
const q = url.searchParams.get('q') ?? '';
return Response.json(await buscarEnIndice(q));
}
Esta asimetría es reveladora: dos rutas de la misma tienda —la ficha casi estática con un hueco fresco, la búsqueda entera dinámica— viven en extremos opuestos del dial porque su relación con el dato es opuesta. No hay un modo de render correcto para la tienda; hay uno correcto para cada una de sus páginas, y diseñar el render es desmenuzar el producto hasta que cada ruta cae, sola, en su casilla.
El error caro con un producto mixto es decidir su render de una sola vez: volverlo todo estático y no poder mostrar el stock, o volverlo todo SSR y pagar cómputo por cada artículo que nunca cambia. La zona gris —una tienda que es mitad catálogo público y mitad cuenta privada— no se resuelve con un interruptor global, sino ruta a ruta, hasta que cada página descansa en el escalón más barato que su naturaleza le permite.
flowchart TD
R[Nueva ruta] --> Q1{El dato cambia en cada peticion}
Q1 -->|no basta el build| EST[Estatico SSG en CDN]
Q1 -->|si| Q2{Es personalizado por usuario}
Q2 -->|no igual para todos| SI[Server island con cache propia]
Q2 -->|si| Q3{Necesita computo pesado o BD cercana}
Q3 -->|no la latencia manda| EDGE[SSR en el edge]
Q3 -->|si| REG[SSR regional serverless o Node]
style EST fill:#a6e3a1,color:#11111b
style SI fill:#94e2d5,color:#11111b
style EDGE fill:#89b4fa,color:#11111b
style REG fill:#fab387,color:#11111bEdge o región: el último matiz
Cuando una ruta es SSR, queda una decisión más: dónde corre ese código. El edge —Workers repartidos por el globo— pone tu cómputo a milisegundos del usuario, ideal para personalización ligera, geolocalización y redirecciones rápidas. La región —una función serverless o un Node en un centro de datos— gana cuando el trabajo es pesado, necesita estar junto a la base de datos, o dura más de lo que un Worker tolera.
La server island añade el matiz más fino de todos: te deja mantener la página estática en el CDN y empujar al servidor solo el fragmento dinámico, con su propia cabecera de caché. Es el dial en su máxima granularidad —no la página o el servidor, sino este trozo aquí, con esta frescura—. Diseñar bien el render es, casi siempre, mantener el máximo de superficie en el extremo barato del dial y escalar quirúrgicamente lo mínimo imprescindible.
Ese fragmento se declara con server:defer sobre una página que sigue horneada: la cáscara es estática y solo el trozo marcado viaja al servidor, con su propio fallback mientras resuelve.
---
// La pagina sigue estatica; solo el fragmento se resuelve en el servidor
export const prerender = true;
---
<article>{articulo.cuerpo}</article>
<Stock server:defer>
<span slot="fallback">Comprobando disponibilidad...</span>
</Stock>
La tentación de poner todo en el edge choca con una realidad física: tu código está a milisegundos del visitante, pero puede estar a océanos de tu base de datos. Un SSR en el edge que hace tres consultas a una base lejana es más lento que uno regional pegado a ella. Por eso el edge brilla con dato cacheado o replicado —KV, lectura desde una réplica cercana— y la región brilla con dato transaccional que vive en un solo sitio. La cercanía que importa no es una, son dos, y a veces tiran en direcciones opuestas.
Cada ruta que sacas de estático deja de ser un fichero servido gratis por el CDN y pasa a ejecutar tu código en cada petición: latencia, cómputo y factura. No es un mal en sí —el carrito debe ser dinámico—, pero volver dinámica una página por comodidad, cuando una server island bastaba, multiplica el coste del sitio entero. El dial tiene un sentido económico: cada escalón cuesta más.
Diseñar el render por página es, en el fondo, dejar de preguntarte cómo funciona Astro y empezar a preguntarte qué es cada página en su esencia. Porque el modo de renderizado no es un ajuste técnico que eliges por gusto: es la consecuencia inevitable de dos verdades sobre la página misma —su relación con el tiempo y su relación con la identidad—. Una página cuyo contenido quedó fijado cuando lo escribiste no tiene ninguna razón para recalcularse en cada visita: su verdad es atemporal dentro de un despliegue, y por eso es estática, horneada una vez y servida infinitas. Una página cuyo dato late —un stock que baja, un precio que sube— tiene una verdad que caduca, y por eso necesita resolverse cerca de la petición, aunque solo sea en el fragmento que late. Y una página que responde distinto según quién pregunta —tu carrito, tu panel— tiene una verdad que pertenece a una identidad y no se puede compartir, y por eso es dinámica y privada por naturaleza. Cuando lo ves así, el mapa de decisión deja de ser una tabla que memorizas y se vuelve una lectura: miras la página, preguntas si su verdad caduca y a quién pertenece, y el modo de render se revela solo. El arquitecto no reparte modos de render por intuición ni por costumbre; los deduce de la naturaleza temporal e identitaria de cada página, y por eso su sitio es rápido no por una optimización tardía, sino porque cada ruta está exactamente donde su esencia manda que esté. Ese es el salto de este nivel: dejar de configurar el render y empezar a deducirlo.
- Elige un producto con al menos cinco tipos de página distintos —una tienda, un periódico digital, un SaaS— y lístalos.
- Para cada uno, responde las dos preguntas: ¿su dato caduca entre build y petición? ¿se personaliza por usuario? Sitúalo en una de las cuatro casillas del cuadrante.
- Asigna a cada página una posición del dial —estático, server island o SSR— y justifícala con las respuestas anteriores.
- Para las rutas SSR, decide edge o región razonando la doble cercanía —al usuario y al dato— y señala qué fragmentos podrías bajar a server island para devolverlos al CDN.