client:only: islas sin servidor y sus riesgos
La directiva que rompe la regla: client:only omite por completo el renderizado de servidor, así que no hay HTML inicial para la isla y hay que declarar el framework a mano porque Astro no puede inferirlo. A cambio de habilitar componentes que solo funcionan en el navegador, se pagan riesgos concretos —salto de layout, ausencia de SEO, destello de vacío— que hay que conocer y mitigar con un fallback.
Todas las directivas vistas hasta aquí comparten un cimiento: la isla se renderiza a HTML en el servidor y el cliente la hidrata sobre ese marcado. client:only es la excepción que rompe ese cimiento. Le dice a Astro que no renderice nada en el servidor para esa isla y que espere a construirla entera en el navegador. Es una herramienta de escape legítima para componentes que solo pueden vivir en el cliente, pero paga un precio en las mismas propiedades —HTML inicial, SEO, estabilidad visual— que hacen valioso a Astro. Usarla bien es saber cuándo ese precio merece la pena.
- Entender que
client:onlyomite el renderizado de servidor por completo. - Saber por qué exige declarar el framework de forma explícita.
- Reconocer sus tres riesgos: salto de layout, SEO nulo y destello de vacío.
- Mitigar con un contenido de
fallbacky espacio reservado.
Qué hace, y por qué pide el framework
Con client:only, Astro trata la isla como puramente de cliente: la omite en el render de servidor y no emite HTML para ella, solo el elemento <astro-island> vacío que el navegador rellenará. Cuando su JavaScript llega, el framework renderiza desde cero —no hidrata, porque no hay marcado previo que reutilizar— y monta el componente en ese hueco.
De ahí sale una peculiaridad de sintaxis obligatoria: tienes que nombrar el framework. Como no hay render de servidor, Astro nunca ejecuta el componente en el build y no puede deducir a qué integración pertenece; se lo dices tú en la directiva.
---
import Editor from '../components/Editor.jsx';
---
<Editor client:only="react" />
El valor —"react", "vue", "svelte", "solid-js" o "preact"— identifica el renderer que el cliente debe cargar. Omitirlo es un error de compilación, no un descuido silencioso: sin ese dato, el runtime no sabría qué pegamento de framework importar para dar vida a la isla.
Nombrar client:only="react" no basta si React no está añadido al proyecto como integración. La directiva le dice a Astro qué renderer cargar, pero ese renderer tiene que existir: se instala con la integración correspondiente —por ejemplo, con npx astro add react— que registra el adaptador de cliente. Sin ella, la isla no tendrá con qué renderizarse en el navegador.
No hace falta un adaptador de servidor ni SSR para usar client:only: como todo su render ocurre en el navegador, encaja igual en un sitio totalmente estático. La directiva no traslada trabajo al servidor —al contrario, se lo quita—; simplemente mueve el render de esa isla del build al cliente. Lo que decides con ella no es dónde corre tu sitio, sino dónde nace el HTML de esa isla concreta: en el build, o en el dispositivo del visitante.
Los tres riesgos
Renunciar al HTML de servidor tiene consecuencias que conviene mirar de frente, porque son justo las que Astro suele evitar.
El primero es el salto de layout. Hasta que el JavaScript llega y renderiza, el espacio de la isla está vacío; cuando el contenido aparece de golpe, empuja lo que tiene alrededor y la página da un brinco. Ese salto degrada el CLS, una de las métricas de Core Web Vitals, y produce esa sensación de página inestable que reacomoda el texto bajo el dedo del usuario.
El segundo es el SEO. Un rastreador que lea el HTML servido no encontrará nada dentro de la isla: ni texto, ni enlaces, ni encabezados, porque ese contenido no existe hasta que se ejecuta el JavaScript. Para cualquier cosa que deba indexarse —un artículo, una ficha de producto, un titular— client:only es una mala elección, porque esconde justo lo que los buscadores necesitan ver.
El tercero es el destello de vacío. Incluso cuando el SEO no importa, el usuario percibe un hueco en blanco que tarda en poblarse, y en conexiones lentas ese vacío puede durar lo suficiente para parecer un error. La interactividad no solo llega tarde: llega después de un intervalo en el que no había absolutamente nada que mirar.
Es fácil confundir “sin servidor” con “ligero”. Es al revés. Una isla client:only no ahorra JavaScript; renuncia al HTML. Todo su contenido pasa a depender del cliente, así que es de las directivas más caras en experiencia inicial: nada se ve hasta que el framework arranca y renderiza. No la elijas por rendimiento —para eso están client:visible y client:media—, sino solo cuando el componente no puede renderizarse en el servidor.
El desajuste como causa raíz
El motivo por el que estos componentes exigen client:only es el desajuste de hidratación. Si Astro los renderizara en el servidor, producirían un HTML —basado en, digamos, un window.innerWidth que en el build no existe— distinto del que el cliente genera al hidratar. El framework detecta esa divergencia y falla ruidosamente. client:only corta el problema de raíz: al no haber HTML de servidor, no hay nada con lo que discrepar. La directiva no es un capricho, sino la respuesta correcta a una incompatibilidad real entre el momento del build y el del navegador.
Cuándo se justifica, y cómo mitigar
client:only existe porque algunos componentes no pueden renderizarse en el servidor. Suelen caer en estos casos:
- Tocan
window,documentolocalStoragenada más montarse. - Miden su contenedor del DOM para dibujarse: un lienzo, un mapa.
- Envuelven una librería que se rompe al ejecutarse fuera del navegador.
- Dependen de una API que solo existe en el cliente.
Para ellos, renderizar en el servidor no es que sea caro, es que no tiene sentido, y client:only es la respuesta correcta. Fíjate en el patrón común: todos necesitan el navegador ya en el primer render, no más tarde en un manejador de eventos. Esa distinción es la línea divisoria: si el navegador solo hace falta al interactuar, la isla puede hidratar de forma normal; si hace falta para dibujar el primer fotograma, client:only es inevitable. Cuando la uses, mitiga sus riesgos con un contenido de fallback: un elemento con slot="fallback" que Astro sí incluye en el HTML inicial y que se muestra hasta que la isla se renderiza. Un esqueleto, un mensaje de carga o —lo más valioso— un bloque del mismo tamaño que el componente final reservan el espacio y suavizan el salto.
---
import Mapa from '../components/Mapa.jsx';
---
<Mapa client:only="react">
<div slot="fallback" class="mapa-placeholder">Cargando mapa…</div>
</Mapa>
Reservar el espacio de antemano
El fallback tapa el destello, pero el salto de layout se combate mejor reservando el hueco con CSS: si el contenedor ya ocupa su tamaño final desde el HTML, la isla se pinta dentro sin empujar a nadie. Una proporción fija o una altura mínima bastan.
.mapa-placeholder {
aspect-ratio: 16 / 9;
display: grid;
place-items: center;
}
Sin servidor
No hay HTML inicial para la isla; el framework renderiza desde cero en el cliente.
Framework explícito
client:only="react" y similares: Astro no puede inferir el renderer sin render de servidor.
Riesgos
Salto de layout, SEO nulo y destello de vacío: los tres nacen de la falta de HTML.
fallback
Un slot="fallback" del tamaño final reserva espacio y calma el CLS mientras carga.
flowchart TB subgraph OTRAS[client load visible media] A[servidor renderiza HTML] --> B[HTML visible e indexable] B --> C[cliente hidrata sobre el marcado] end subgraph SOLO[client only] D[servidor no renderiza nada] --> E[hueco vacio sin contenido] E --> F[cliente renderiza desde cero] F --> G[riesgo de salto de layout] end style B fill:#a6e3a1,color:#11111b style E fill:#f38ba8,color:#11111b style G fill:#f38ba8,color:#11111b
Antes de aceptar ese precio, descarta las señales de que en realidad no la necesitas:
- El componente se renderiza igual en servidor y cliente: usa otra directiva.
- Solo lees
windowdentro de un manejador de eventos, no al montar: puede hidratar normal. - Lo que buscas es contenido dinámico, no interacción: una server island basta.
- El dato que falta en el servidor puede llegar como prop: pásalo y renderiza en servidor.
En 2026 hay además una alternativa que conviene sopesar antes de recurrir a client:only: las server islands con server:defer. Si lo que necesitas es contenido dinámico —personalizado, fresco, dependiente de la petición— pero no interactividad de navegador, una server island lo genera en el servidor y lo inyecta sin enviar JavaScript, dejando el resto de la página cacheable. client:only es para lo que de verdad exige el navegador; para lo que solo es dinámico, casi siempre hay una opción más barata.
La existencia misma de client:only revela lo que Astro te da en todas las demás directivas y que es fácil dar por sentado: un HTML de servidor que es útil por sí solo. En client:load, client:idle, client:visible y client:media, el JavaScript es una mejora que se posa sobre un marcado que ya funcionaba —visible, indexable, estable— y por eso el peor caso de esas directivas, que su JavaScript falle o tarde, degrada con elegancia: te quedas con el HTML. client:only retira esa red. Al omitir el servidor, hace que el JavaScript pase de mejora a requisito: si no llega, no hay nada; si tarda, hay un vacío; si un buscador no lo ejecuta, no ve contenido. Esta directiva es, en el fondo, un medidor: cada vez que te descubres alcanzándola, la pregunta correcta no es “¿cómo la uso?”, sino “¿por qué este componente no puede darme HTML?”. A veces la respuesta es legítima e irreductible —el componente vive de una API que solo existe en el navegador— y entonces client:only es la herramienta exacta, con su fallback puesto para tapar los agujeros que abre. Pero muchas otras veces la respuesta es que arrastras un hábito del mundo del todo-al-cliente, donde renderizar en el navegador era lo normal y no una renuncia. La disciplina consiste en tratar client:only como lo que es: no un atajo, sino una excepción que declara, en cada uso, que aceptas perder el servidor para esa isla. Cuanto menos la necesites, más partido le estás sacando a la arquitectura que pagaste al elegir Astro.
- Renderiza un componente con
client:only="react"y confirma en el HTML servido que no hay contenido dentro de su<astro-island>. - Simula una conexión lenta en el navegador y observa el destello de vacío y el salto de layout cuando por fin hidrata.
- Añade un
slot="fallback"del mismo tamaño que el componente final y comprueba cómo desaparece el salto. - Para ese mismo componente, argumenta si necesita de verdad el navegador o si una server island con
server:deferbastaría.