Email copiado — support@tuurt.com
Cargando experiencia
astro · 25 de agosto de 2026 · 6 min

Astro SSR con islands: cuándo hidratar un componente y cuándo dejarlo estático

Astro no hidrata nada por defecto: cada componente interactivo es una decisión explícita. Repasamos client:load, client:idle, client:visible y el error más común: hidratar componentes que nunca necesitaron JavaScript en el cliente.

Por Equipo Tuurt

Astro SSR con islands: cuándo hidratar un componente y cuándo dejarlo estático

Cada vez que migramos un proyecto hacia Astro, la primera pregunta que nos hacen los equipos que vienen de React o Vue puro es la misma: "¿dónde pongo el client:load?". La respuesta correcta casi siempre es "en ningún lado, todavía", y esa respuesta suele sorprender. Astro invierte la suposición por defecto de los frameworks orientados a SPA: un componente .astro, o un componente de React/Vue/Svelte importado dentro de una página .astro, se renderiza en el servidor a HTML puro y no envía ni un byte de JavaScript al navegador a menos que se le indique explícitamente lo contrario. La arquitectura de islands no es una optimización que se activa después; es el comportamiento por defecto, y el trabajo real está en decidir qué componentes rompen esa regla y por qué.

Qué es una isla y qué significa hidratar

En Astro, cada componente interactivo que se renderiza en el cliente es una "isla": un fragmento de HTML servido desde el servidor que, en algún momento después de la carga inicial, se hidrata con el framework que lo generó (React, Vue, Svelte, Solid) para volverse interactivo. El resto de la página —el layout, el texto, las imágenes, cualquier componente .astro puro— nunca se hidrata porque nunca tuvo JavaScript asociado: es HTML estático servido tal cual, sin runtime de ningún framework corriendo en el navegador.

Esto es distinto de lo que ocurre en un framework SSR clásico como Next.js en modo pages o una SPA con hidratación completa, donde toda la página se hidrata como una sola unidad aunque el 90% del contenido sea texto estático que nunca cambia. En Astro, la hidratación es una decisión por componente, no una decisión global de la página, y esa granularidad es la que permite que una landing page con un formulario de contacto envíe JavaScript solo para el formulario, no para el hero, el footer ni la sección de testimonios.

Las directivas client:*

Un componente de framework importado en un archivo .astro no se hidrata automáticamente. Sin ninguna directiva, Astro lo renderiza a HTML en build time o request time y descarta el componente interactivo: lo que llega al navegador es marcado estático, sin <script> asociado.

---
import Carrito from '../components/Carrito.jsx';
---
<Carrito items={items} />

Este Carrito se renderiza una vez en el servidor y queda congelado: los botones de "agregar" o "quitar" no van a responder a ningún click porque no hay JavaScript de React corriendo en esa página. Para que se hidrate, hay que ser explícito:

<Carrito items={items} client:load />

client:load hidrata el componente en cuanto el HTML de la página termina de cargar, con la misma prioridad que un <script> normal sin defer. Es la directiva correcta para lo que el usuario necesita interactivo desde el primer instante: un carrito de compras visible above the fold, un formulario de login, un menú de navegación con estado.

client:idle retrasa la hidratación hasta que el navegador reporta un momento ocioso, usando requestIdleCallback con un fallback a un setTimeout corto en navegadores que no lo soportan. Tiene sentido para componentes interactivos que están en la página pero no son la prioridad inmediata del usuario: un widget de chat, un selector de idioma, un modal que se abre con un click posterior.

<WidgetChat client:idle />

client:visible usa un IntersectionObserver y no hidrata nada hasta que el componente entra en el viewport. Es la directiva que más impacto tiene en páginas largas: un carrusel de productos relacionados al final de una página de producto, comentarios, un mapa embebido. Si el usuario nunca hace scroll hasta ahí, ese JavaScript nunca se descarga ni se ejecuta.

<Comentarios postId={post.id} client:visible />

Existen además client:media (hidrata solo si un media query coincide, útil para componentes que solo tienen sentido en mobile) y client:only="react" (salta el renderizado en servidor por completo y renderiza únicamente en el cliente, necesario para componentes que dependen de APIs del navegador como window o localStorage y que romperían el SSR).

El error que más repetimos: hidratar sin necesidad

El error común no es elegir la directiva equivocada, es agregar client:load a un componente que nunca necesitó ejecutarse en el navegador. Pasa con frecuencia en dos escenarios concretos.

El primero es migrar componentes de React tal cual desde un proyecto anterior, arrastrando el hábito de que "todo componente de React necesita hidratarse para verse bien". Una tarjeta de producto que solo muestra imagen, precio y un enlace no necesita ningún JavaScript en el cliente: es contenido, no interacción. Envolverla en client:load porque está escrita en JSX infla el bundle sin ninguna ganancia real, y en una página de listado con treinta tarjetas, esos treinta componentes hidratándose de forma redundante son el motivo más común por el que una migración a Astro no logra la mejora de rendimiento que se esperaba.

El segundo es un componente que sí tiene una parte interactiva, pero la interactividad es pequeña frente al resto del componente. Un artículo de blog con un botón de "me gusta" al final no necesita que el artículo completo sea un componente de React hidratado; necesita que el botón lo sea. La solución no es "hidratar todo el bloque", es extraer el botón a su propio componente pequeño y dejar el resto como Astro estático:

---
import BotonMeGusta from '../components/BotonMeGusta.jsx';
---
<article>
  <h1>{post.title}</h1>
  <div set:html={post.contentHtml} />
  <BotonMeGusta postId={post.id} client:visible />
</article>

Este patrón —aislar la interactividad real en el componente más pequeño posible y dejar todo lo demás como marcado estático— es la diferencia entre un proyecto en Astro que efectivamente reduce el JavaScript enviado al cliente y uno que termina pareciéndose a una SPA tradicional con pasos extra.

Props que se serializan y el costo que no se ve en el código

Hay un costo adicional que no aparece al mirar la directiva sino al mirar los props que se le pasan al componente hidratado. Cualquier prop que reciba un componente con client:* tiene que serializarse a JSON e incrustarse en el HTML de la página para que el framework del cliente pueda reconstruir el componente con el mismo estado inicial. Pasar un array de mil productos como prop a un componente con client:load no solo hidrata JavaScript innecesario, también incrusta esos mil productos serializados en el HTML de la página, duplicando datos que ya estaban ahí en forma de marcado renderizado.

La corrección habitual es que el componente hidratado reciba solo lo mínimo que necesita para su propia lógica —un id, un estado inicial pequeño— y que haga su propio fetch si necesita más datos, en lugar de recibir por props todo el dataset que ya se usó para renderizar el HTML estático alrededor.

Cómo lo decidimos en la práctica

Antes de agregar cualquier directiva client:*, la pregunta que hacemos es literal: ¿qué evento del navegador tiene que manejar este componente que el HTML estático no puede manejar por sí solo? Si la respuesta es "ninguno", el componente se queda sin directiva, renderizado como HTML puro. Si la respuesta involucra un click, un input, un scroll que dispara una animación con estado, o una suscripción a un websocket, entonces sí necesita hidratarse, y la directiva se elige según cuándo el usuario realmente necesita esa interacción disponible: inmediatamente (client:load), quince minutos después de haber cargado la página conceptualmente (client:idle), o solo si llega a verlo (client:visible). El resultado, cuando se aplica de forma consistente en un proyecto completo, es una página que puede tener veinte componentes de framework escritos en el código fuente y enviar JavaScript activo para dos o tres de ellos en el navegador real.

astro ssr performance javascript
← Volver al blog