Astro, SSR y client:load: cómo elegir la mejor arquitectura para tu web
Astro es un framework moderno pensado para ofrecer rendimiento, flexibilidad e integración entre HTML estático y componentes dinámicos.
Pero una de las dudas más comunes que surgen al usarlo es:
“¿Debo usar SSR, o puedo quedarme con S3 + CloudFront y client:load si consumo APIs?”
Vamos a explicarlo paso a paso y con ejemplos reales.
🌍 ¿Qué es Astro realmente?
Astro combina lo mejor de los sitios estáticos (SSG) con lo mejor del renderizado dinámico (SSR).
Por defecto, cuando ejecutas astro build, tu proyecto se convierte en HTML estático, ultra rápido y fácil de desplegar en servicios como S3 + CloudFront, Netlify o Vercel.
SSG (Static Site Generation) por defecto en astro
🔍 Evidencia en astro.config.mjs:
export default defineConfig({
site: 'https://example.com',
integrations: [mdx(), sitemap(), react()],
// ❌ NO hay output: 'server' o output: 'hybrid'
});
¿Qué significa esto?
- ✅ Las páginas se generan en build time (npm run build)
- ✅ Genera archivos HTML estáticos
- ✅ Los datos de Strapi se fetching durante el build
- ❌ Los datos NO se actualizan hasta el próximo build
- ✅ Ultra rápido en producción (solo HTML estático)
- ✅ Puedes deployar en Netlify, Vercel, Cloudflare Pages, etc.
Pero Astro también permite que partes de tu sitio sean interactivas.
Esto se logra con directivas como client:load, client:idle o client:visible, las cuales indican que ciertos componentes (por ejemplo, uno con React o Vue) deben hidratarse en el cliente.
⚡ client:load: interactividad sin servidor
Cuando usas client:load, tu componente React se ejecuta directamente en el navegador del usuario, no en el servidor.
Esto significa que puedes tener hooks (useState, useEffect, etc.), manejar eventos y consumir APIs públicas.
Por ejemplo:
🌗 ThemeToggle.jsx
import { useState, useEffect } from "react";
export default function ThemeToggle() {
const [theme, setTheme] = useState("light");
useEffect(() => {
const t = localStorage.getItem("theme") || "light";
setTheme(t);
document.documentElement.classList.toggle("dark", t === "dark");}, []);
const toggle = () => {
const t = theme === "light" ? "dark" : "light";
setTheme(t):
localStorage.setItem("theme", t);
document.documentElement.classList.toggle("dark", t === "dark");
};
return <button onClick={toggle}>{theme === "dark" ? "🌙" : "☀️"}</button>;
}
// Renderizar datos de una API en el servidor
const res = await fetch("https://api.example.com/posts");
const posts = await res.json();
---
<ul>
{posts.map(post => <li>{post.title}</li>)} </ul>
📄 index.astro
import ThemeToggle from "../components/ThemeToggle.jsx";
---
<ThemeToggle client:load />
✅ Este componente funciona perfectamente desplegado en S3 + CloudFront,
ya que todo ocurre en el navegador del usuario.
No necesitas un servidor activo ni SSR.
🧠 Entonces, ¿qué es SSR y cuándo se usa?
SSR significa Server-Side Rendering, o renderizado en el servidor.
Esto implica que, cada vez que alguien entra a tu sitio, el servidor ejecuta tu código, genera el HTML dinámicamente y lo envía ya listo al navegador.
En otras palabras:
- El usuario entra a una página.
- El servidor (por ejemplo una Lambda, EC2 o Vercel Function)
- El navegador recibe ese HTML ya armado, junto con los estilos CSS.
- Luego, se hidrata el JavaScript para dar interactividad
Luego, se hidrata el JavaScript para dar interactividad.
¿Quieres datos dinámicos? Opciones:
// astro.config.mjs
export default defineConfig({
output: 'server', // ← Cambiar a SSR
adapter: node(), // Necesitas un adapter (node, vercel, netlify, etc.)
// ...
});
- ✅ Datos siempre actualizados (fetches en cada request)
- ❌ Más lento (server rendering)
- ❌ Requiere servidor Node.js
Opción 2: Hybrid - Mezcla de SSG + SSR
// astro.config.mjs
export default defineConfig({
output: 'hybrid', // ← SSG por defecto, SSR opcional
adapter: node(),
});
// En las páginas que quieras SSR:
export const prerender = false; // ← Hace esta página SSR
--------------------------------------------------
🕒 Ejemplo práctico: Strapi + Astro
Imagina que tienes un blog conectado a Strapi (tu CMS).
Si tu página usa SSG (Static Site Generation), Astro genera los HTML durante el astro build:
---
const res = await fetch("https://api.mi-strapi.com/posts");
const posts = await res.json();
---
<ul>
{posts.map(post => <li>{post.title}</li>)}
</ul>
✅ Este HTML se genera una sola vez y luego se despliega a S3 + CloudFront.
Pero si agregas un nuevo post en Strapi, no aparecerá hasta que hagas otro build y vuelvas a subir tu sitio.
--------------------------------------------------
⚙️ En cambio, con SSR:
Si configuras Astro con output: "server" o usas server:defer, cada visita al sitio ejecuta el fetch en tiempo real:
---
const res = await fetch("https://api.mi-strapi.com/posts");
const posts = await res.json();
---
<ul>
{posts.map(post => <li>{post.title}</li>)}
</ul>
✅ Ahora, cada vez que alguien entre, Astro pedirá los posts actualizados a Strapi.
Si creas un nuevo post, aparecerá de inmediato, sin rebuild.
⚖️ ¿Y qué pasa si uso APIs REST con client:load?
Si tu página usa client:load para consumir una API (por ejemplo, para mostrar datos dinámicos en el cliente), no necesitas SSR.
Tu sitio puede seguir siendo estático y desplegarse en S3 + CloudFront.
El flujo sería así:
- Astro genera HTML básico en el build.
- Cuando el usuario entra, el componente React se hidrata.
- Desde el navegador, tu componente hace un fetch() a tu API REST.
- Los datos se muestran dinámicamente, sin necesidad de servidor Astro.
💡 Ideal para apps ligeras o dashboards donde el navegador puede manejar los fetchs directamente.
Esto es útil cuando necesitas:
- Interactividad (botones, inputs, sliders, animaciones).
- Cargar datos desde el navegador, por ejemplo, al consumir APIs privadas.
- Mejorar la UX sin recargar toda la página.
Astro también ofrece:
- client:visible → carga el componente cuando entra en el viewport.
- client:idle → carga cuando el navegador está libre.
- client:only → carga el componente solo en el cliente (no SSR)
⏱️ Pero... ¿qué pasa si quiero actualizar sin rebuild?
Ahí entra en juego SSR o revalidación dinámica.
Si tu web consume datos de Strapi y quieres ver los cambios al instante, sin ejecutar astro build otra vez, necesitas:
1. ISR con webhooks (ISR (Incremental Static Regeneration), donde Astro reconstruye ciertas páginas cada X minutos.):
- Deploy en Vercel/Netlify
- Configura webhook en Strapi → rebuild on publish
- Mantiene SSG (rápido) + datos frescos
2. Hybrid mode (más control(SSR con output: "server" → cada request pide los datos nuevos):
- index.astro → SSR (datos frescos)
- Otras páginas → SSG (rápidas)
👉La opción ISR es un punto medio: sigues desplegando en S3 + CloudFront, pero se regeneran las páginas bajo demanda (con ayuda de un webhook o una función lambda).
Comparando dónde desplegar SSR: AWS, Vercel y Cloudflare
Cuando usas SSR o server:defer (streaming en Astro), necesitas un entorno que pueda ejecutar código en el servidor o en el edge (borde). Aquí entran en juego tres plataformas principales:
por tanto
- Vercel: Ideal para SSR de Astro sin complicaciones. Tiene edge functions listas para server:defer.
- Cloudflare: Excelente para proyectos que priorizan latencia global mínima y costos bajos.
- AWS: Mayor flexibilidad y control, pero requiere definir tu arquitectura (Lambda, EC2, Fargate, etc.).
🧭 Conclusión
Si tu sitio Astro:
- No usa server:defer,
- Consume APIs desde el cliente (client:load),
- Y no necesita actualizar el contenido en tiempo real...
Entonces no necesitas SSR.
Puedes desplegar tranquilamente en S3 + CloudFront y mantener tu sitio rápido, económico y escalable.
Pero si tu proyecto requiere actualización inmediata de datos (por ejemplo, contenido de Strapi que cambia seguido o dashboards en vivo),
entonces sí, SSR es obligatorio, ya que S3 solo sirve archivos estáticos y no ejecuta lógica del servidor.