Islas en Astro: por qué tu web carga al instante
Cómo funcionan las islas de Astro, qué cambia cuando son componentes de Vue, y por qué tu web carga antes en un móvil con mala cobertura.

La mayor parte de tu web es un cartel impreso: aparece al instante porque no hay nada que ejecutar.
Solo las partes que de verdad tienen que hacer algo reciben maquinaria. El formulario de reserva, la galería, el filtro del catálogo. Y la reciben cuando el visitante llega a ellas, no antes.
El enfoque habitual hace lo contrario: manda la maquinaria de la página completa, así que el móvil del visitante tiene que montarla entera antes de mostrar nada. De ahí vienen esos dos segundos de pantalla en blanco en el bar con mala cobertura.
Astro llama islas a esas partes vivas. Todo lo demás es HTML y se queda quieto. Ese es el modelo entero. El resto del artículo es cómo se controla y qué cambia cuando esas islas son componentes de Vue.
Qué es una isla, técnicamente
En Astro el punto de partida es cero JavaScript. Todo se renderiza a HTML en el build, y un componente de Vue que sueltas en una página se queda en HTML estático salvo que le digas explícitamente lo contrario.
Esa instrucción explícita es una directiva client:*:
---
import Hero from "../components/Hero.astro";
import Reservas from "../components/Reservas.vue";
---
<Hero />
<Reservas client:visible />
Hero nunca manda un byte de JavaScript al navegador. Reservas sí, pero solo ese componente, y solo cuando entra en pantalla. Eso es una isla: un componente por el que has pagado, rodeado de marcado inerte.
La inversión mental es esta: en una SPA la pregunta es «qué partes puedo hacer estáticas». En Astro es «qué partes necesitan de verdad estar vivas». La respuesta honesta, en una web de negocio, suele ser dos o tres.
Las directivas, ordenadas por utilidad real
La directiva que eliges importa más de lo que la gente le concede. No cambia si pagas JavaScript, cambia cuándo, y eso es precisamente lo que miden las Core Web Vitals.
| Directiva | Cuándo se hidrata | Para qué sirve de verdad |
|---|---|---|
client:load | Inmediatamente al cargar | Lo visible y crítico: un buscador en el hero. Es el que todo el mundo usa por defecto, casi siempre de más. |
client:idle | Cuando el navegador queda libre | Interacción que está arriba pero no es urgente. |
client:visible | Cuando el componente entra en pantalla | Tu opción por defecto para cualquier cosa por debajo del primer scroll. |
client:media="(max-width: 48em)" | Solo si se cumple el media query | Un menú móvil no tiene nada que hacer en escritorio. Muy poco usado. |
client:only="vue" | Solo en cliente, sin HTML previo | Válvula de escape para dependencias que solo existen en navegador. Pagas un hueco visible mientras carga. |
server:defer | En el servidor, por petición, tras el HTML inicial | Un bloque personalizado dentro de una página cacheada: stock real, precio por usuario. Requiere adaptador SSR. |
server:defer es el que más cambia las cosas y el menos conocido. Hasta que existió, un único dato personalizado te obligaba a renderizar la página entera por petición. Ahora el cascarón se sirve estático y el agujero se rellena aparte.
Cinco cosas que te van a morder con Vue
Ordenadas por frecuencia con la que pasan, no por gravedad.
1. Cada isla es su propia app de Vue
Más o menos un createApp por isla. Así que los plugins no existen hasta que los registras, y lo hacen isla por isla. Para eso está appEntrypoint en @astrojs/vue:
// astro.config.mjs
vue({ appEntrypoint: "/src/pages/_app" });
// src/pages/_app.ts
import type { App } from "vue";
import { createPinia } from "pinia";
export default (app: App) => {
app.use(createPinia());
};
Si te lo saltas, el síntoma es un plugin misteriosamente undefined. No hay error claro que te lleve hasta aquí.
2. El estado no cruza de una isla a otra
Esta es la trampa conceptual grande viniendo de Vue. Dos islas son dos instancias de Pinia, sin store compartido. Y no falla: el estado simplemente está separado, en silencio, hasta que alguien prueba el carrito.
Tres salidas, de peor a mejor:
- Eventos personalizados entre islas. Funciona, se ensucia rápido.
- nanostores, que es la recomendación de Astro: un store pequeño, agnóstico del framework, que vive fuera de las islas y al que todas se suscriben.
- Reestructurar para que los componentes que comparten estado vivan dentro de una isla, con un wrapper de Vue. Suele ser la respuesta correcta y la gente se resiste porque parece rendirse.
3. Las props se serializan
De .astro a una isla de Vue todo pasa por serialización. Sin funciones, sin instancias de clase, sin refs. Pasas datos, no comportamiento.
Los slots funcionan, y desde un archivo .astro puedes meter dentro de una isla de Vue otro componente con su propia directiva client:*. Pero cada uno sigue siendo una isla independiente, con lo que eso implica (punto 2): no comparten estado. Y lo que no puedes hacer es importar un componente .astro dentro de un .vue: el contenido de Astro solo entra por slot, desde el .astro, y llega como HTML estático. Anidar interactividad a través de esa frontera es donde se van las tardes.
4. La primera isla paga el runtime entero
El runtime de Vue se comparte entre todas las islas de una página, pero la primera lo paga completo: del orden de 30-40 kB comprimidos antes de tu código.
Lo que convierte la pregunta real en otra. No es client:load o client:visible, es ¿esto necesita Vue?
5. La mitad de lo que haces con Vue ya es HTML
Un acordeón es <details>. Un tooltip o un menú flotante es la Popover API. Un toggle son quince líneas en una etiqueta <script>. Unas pestañas se hacen con :has() y un radio oculto si te apetece jugar.
Guarda Vue para lo que tiene estado de verdad y varios caminos posibles: un configurador, un filtro con múltiples facetas, un formulario por pasos con validación. Para abrir y cerrar una cosa, la plataforma ya lo hace y gratis.
Cuándo las islas dejan de ser el modelo correcto
Las islas brillan en webs dirigidas por contenido con bolsas de interactividad: páginas de servicio, un widget de reservas, un listado filtrable, una ficha de producto. Es decir, casi todo lo que necesita un negocio.
Se vuelven incómodas cuando la página es la aplicación. Un panel de control, un configurador de varios pasos, cualquier pantalla con estado atravesando todo. Ahí te pasas el día sincronizando islas y peleando con la arquitectura en lugar de usarla.
La señal de alarma es concreta: si te encuentras montando un store compartido para que cuatro islas se enteren de lo mismo, ya no tienes islas. Tienes una SPA troceada, con el coste de las dos cosas y las ventajas de ninguna.
Llegado ese punto hay dos salidas honestas. Una isla grande que envuelva toda la pantalla, y Astro pasa a ser el servidor de HTML que la entrega. O la herramienta que corresponde: en mi caso, Laravel con Inertia y Vue, donde el estado compartido es el trabajo del framework y no tu problema.
Qué significa esto si lo que tienes es un negocio
Volvamos al cartel impreso, porque la parte técnica solo importa por lo que produce.
Tus visitantes no navegan con un portátil nuevo y fibra. Entran con un móvil de hace tres años, en la calle, con una barra y media de cobertura, probablemente buscando un teléfono o un horario. En esas condiciones la diferencia entre una página que pinta en medio segundo y otra que tarda tres no es una métrica: es si te llaman o llaman al siguiente.
Tres consecuencias concretas:
- Menos abandono antes del primer pintado. La gente no espera a una pantalla en blanco; vuelve atrás y prueba otro resultado.
- Mejores Core Web Vitals, que Google usa como señal. No es el factor que decide tu posición, pero sí uno de los pocos que está enteramente en tu mano.
- Menos cosas que se rompen. Una página que es HTML no deja de funcionar porque un script falle o tarde. Lo que no se ejecuta no se cae.
Y un aviso para equilibrar: esto no arregla fotos de 4 MB, ni seis scripts de terceros, ni una ficha de Google abandonada. La arquitectura te da un suelo rápido. Encima de ese suelo todavía se puede construir algo lento.
Cómo decido, componente a componente
Cuatro preguntas, en este orden. La primera descarta la mayoría de los casos.
- ¿Necesita JavaScript? Si
<details>, la Popover API o quince líneas de vanilla lo resuelven, no es una isla. Fin. - ¿Tiene estado de verdad? Varios caminos posibles, validación, datos que cambian mientras el usuario mira. Si la respuesta es sí, Vue se gana el sitio.
- ¿Está visible al cargar? No →
client:visible. Sí y es crítico →client:load. Solo en móvil →client:media. - ¿Comparte estado con otro componente? Si sí, los dos van en la misma isla. Si no puede ser, nanostores. Pinia por isla solo cuando ese estado de verdad no sale de ahí.
Una nota de versiones: Astro 7.4 salió el 15 de octubre de 2026 y trae astro build --timings, que te dice en qué fase se gasta el build. Si vas a auditar un proyecto con islas, empieza por ahí antes de tocar nada. Sube el mínimo de Node a 22.22.0, así que mira eso primero en repos antiguos.
Si tienes una web lenta y sospechas que el problema es arquitectónico y no de fotos, la reviso contigo. Y si lo que tienes es una web que todavía no existe, mejor: empezar así sale más barato que arreglarlo después. Así es como trabajo el diseño web a medida, y si quieres hablarlo, escríbeme.
Nota: este artículo lo he escrito en colaboración con mis agentes de IA. Ellos investigaron, revisaron y contrastaron con la documentación; yo decidí qué se quedaba, corregí lo que sonaba a folleto y les quité las ganas de usar la palabra «potente». Las opiniones son mías. Ninguna isla resultó herida durante la redacción. 🏝️🤖

Alessandro Giuzio
Temas relacionados: