La solución de Astro: HTML por defecto
Astro invierte la regla: HTML estático primero, cero JavaScript salvo en las islas que tú declaras. El enfoque content-first y a quién beneficia de verdad: blogs, docs, e-commerce y marketing.
Si el problema es enviar una aplicación para servir un documento, la solución es dejar de hacerlo. Astro renderiza tus componentes a HTML en el servidor, no envía ningún runtime por defecto, y solo hidrata las porciones interactivas que tú marcas explícitamente. El resultado es una web que carga como lo que es: contenido.
- Entender el modelo “HTML por defecto, cero JS” y cómo lo consigue Astro.
- Ver cómo un componente
.astrose ejecuta en el servidor y nunca llega al cliente. - Declarar interactividad con las directivas
client:*sobre componentes de framework. - Reconocer el enfoque content-first y a quién beneficia.
HTML por defecto, cero JavaScript
Un componente .astro es plantilla HTML con un bloque de script delimitado por --- (la “valla frontmatter”). Ese bloque corre en el servidor, durante el build o en la petición, y jamás se envía al navegador. Lo que el usuario recibe es el HTML resultante, sin runtime, sin framework, sin bundle de hidratación.
---
// Este bloque se ejecuta en el servidor. Nunca llega al navegador.
const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));
const destacados = posts.filter((p) => p.frontmatter.destacado);
---
<section>
<h1>Últimos artículos</h1>
<ul>
{destacados.map((p) => (
<li><a href={p.url}>{p.frontmatter.title}</a></li>
))}
</ul>
</section>
Fíjate en lo que ocurre: usas expresiones de JavaScript para construir la página, pero el JavaScript se queda en el servidor. Al cliente solo baja la lista ya renderizada en HTML. La lógica se evapora en el build; el usuario nunca la descarga.
La diferencia con “optimizar el bundle” es categórica. En Astro, una página sin componentes interactivos envía exactamente 0 KB de JavaScript. No es un bundle pequeño: es la ausencia de bundle. Añadir interactividad es una decisión consciente y localizada, no el punto de partida que luego intentas recortar.
Las islas: JavaScript solo donde lo pides
¿Y cuando sí necesitas interactividad —un buscador, un carrusel, un botón con estado—? Astro te deja usar componentes de React, Vue, Svelte, Solid o Preact, y los convierte en islas: fragmentos interactivos embebidos en un océano de HTML estático. Cada isla se activa con una directiva client:* que decide cuándo se hidrata.
---
import Buscador from '../components/Buscador.jsx';
import Carrusel from '../components/Carrusel.svelte';
---
<article>
<h1>Guía de Astro</h1>
<p>Todo este texto es HTML estático: cero JavaScript enviado.</p>
<!-- Solo estas dos islas se hidratan, cada una por su cuenta -->
<Buscador client:load />
<Carrusel client:visible />
</article>
Sin la directiva, un componente de framework se renderiza a HTML y se queda inerte, igual que un .astro. Con ella, y solo entonces, Astro envía el JS mínimo para esa isla concreta. El resto de la página no paga nada.
client:load
Hidrata en cuanto carga la página. Para lo crítico e inmediato: un botón de compra visible al entrar.
client:idle
Espera a que el navegador esté ocioso. Para lo interactivo pero no urgente.
client:visible
Hidrata al entrar en el viewport. Ideal para islas que viven abajo, en el fold inferior.
client:media
Hidrata solo si se cumple una media query. Por ejemplo, un menú que solo existe en móvil.
La regla de oro es la parsimonia: cada directiva que añades es JavaScript que el usuario descargará. client:load es tentador porque “siempre funciona”, pero convierte cada isla en coste inmediato. La mayoría vive bien con client:visible o client:idle, que difieren el trabajo hasta que de verdad hace falta.
El peligro al empezar es hidratarlo todo por comodidad y reconstruir sin querer una SPA, isla a isla. Si marcas cada componente con client:load, has vuelto al punto de partida: mucho JavaScript, mucho bloqueo. Una isla debe justificar su existencia. Ante la duda, déjalo como HTML estático y añade interactividad solo cuando el diseño la exija de verdad.
Content-first: el contenido es de primera clase
Astro no trata el contenido como un añadido: lo pone en el centro. Las content collections te dejan definir un esquema tipado para tu Markdown y MDX, validado con Zod, de modo que tu frontmatter tiene tipos y autocompletado como cualquier otra estructura de datos.
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
schema: z.object({
title: z.string(),
fecha: z.date(),
destacado: z.boolean().default(false),
}),
});
export const collections = { blog };
MDX permite mezclar componentes dentro de la prosa —esta misma guía lo hace—. El flujo entero, desde el fichero de contenido hasta el HTML final, está pensado para escribir mucho y enviar poco.
flowchart LR
A[Componentes .astro] --> B[Render en el servidor]
B --> C[HTML puro sin JS]
D[Componente de framework] --> E{Lleva directiva client}
E -- si --> F[Isla hidratada]
E -- no --> C
style C fill:#a6e3a1,color:#11111b
style F fill:#89b4fa,color:#11111bA quién beneficia
El modelo brilla justo donde la SPA desperdicia. Cualquier sitio donde el contenido manda y la interactividad es puntual gana con Astro.
Blogs y publicaciones
Texto e imágenes: el terreno natural del HTML. Cero JS por artículo, SEO impecable.
Documentación
Miles de páginas estáticas con búsqueda como isla. Starlight, el tema de docs de Astro, vive de esto.
E-commerce
Catálogo y fichas de producto estáticos y rapidísimos; carrito y checkout como islas interactivas.
Marketing y landings
Páginas que deben cargar en un parpadeo para no perder conversión. Cada milisegundo cuenta.
El giro mental de Astro es quién tiene que justificarse. En una SPA, el JavaScript es el estado por defecto y la ausencia de JS es la excepción que hay que pelear con optimizaciones. Astro invierte la carga de la prueba: el HTML estático es el estado por defecto y cada kilobyte de JavaScript hay que justificarlo con una directiva explícita. No es una diferencia de grado, sino de dirección. Cuando cada isla es una decisión visible en el código —client:load, client:visible—, el rendimiento deja de ser una fase de “optimización” al final del proyecto y se convierte en una propiedad estructural que tienes que romper a propósito. Es mucho más difícil hacer una web lenta con Astro que hacerla rápida. Ese es, en una frase, todo el valor del framework.
- Elige una página que conozcas bien —tu blog, la home de un producto— y dibújala en papel.
- Marca con color solo las zonas que necesitan JavaScript de verdad: un buscador, un menú, un slider.
- Cuenta qué porcentaje de la página queda sin marcar: eso es HTML puro que Astro enviaría con cero JS.
- Asigna a cada zona marcada la directiva que le tocaría: ¿
client:load,client:idleoclient:visible? Justifica por qué.