.astro frente a componentes de framework
Cuándo usar un componente .astro y cuándo uno de framework como React o Solid: el contraste entre renderizar HTML en el build y ofrecer interactividad en el cliente, por qué los .astro no son reactivos por diseño, y cómo las directivas client convierten un componente en una isla hidratada.
Dos clases de componente
Un componente .astro y uno de React resuelven problemas distintos, aunque ambos se llamen componente. El .astro es una plantilla de servidor: se ejecuta en el build o en la petición, emite HTML y no lleva ningún runtime al cliente. Un componente de framework describe una interfaz que puede ejecutarse en el navegador, con estado, eventos y repintado —y para hacerlo necesita que su librería viaje al cliente—.
Por defecto, y esto sorprende a muchos, Astro renderiza también los componentes de framework a HTML estático en el servidor. Un <Contador /> de React colocado sin más en una página produce su marcado inicial y nada de JavaScript: se comporta como un .astro. La interactividad no viene de escribir React, sino de pedirla explícitamente con una directiva.
---import Precio from '../components/Precio.astro'; // plantilla de servidorimport Cesta from '../components/Cesta.tsx'; // componente de React---<Precio valor={39} /><Cesta /> <!-- se renderiza a HTML, aun sin interactividad -->En esta página conviven las dos clases sin fricción. Precio solo puede ser HTML: no existe otra vía. Cesta, en cambio, es HTML por ahora, pero guarda un potencial que Precio nunca tendrá: si se lo pides, correrá en el navegador. Escribir el componente en React no activa ese potencial; escribir la directiva, sí.
El matiz decisivo es que la frontera no está en el lenguaje sino en el destino de ejecución. Un componente de framework sin hidratar y un .astro producen, para el visitante, exactamente lo mismo: HTML inerte. Lo que los separa no es cómo los escribiste, sino si alguna vez llegan a ejecutarse en el navegador. Escribir en React es una condición necesaria para la interactividad, pero nunca suficiente por sí sola.
No reactivo por diseño
Un .astro no reacciona en el cliente, y conviene decirlo con precisión: no es que Astro no haya sabido hacerlo reactivo, es que su plantilla no describe una relación viva entre estado y marcado. Como viste, sus expresiones se evalúan una vez y quedan impresas. No hay useState, no hay repintado, no hay listeners que sobrevivan al renderizado, porque el modelo entero es una proyección de datos a HTML que ocurre y termina antes de que exista el navegador.
Esto es una elección de diseño, no una limitación. La inmensa mayoría del contenido de una web no cambia tras cargarse —textos, cabeceras, listados, artículos— y para todo eso la reactividad es peso muerto: un motor que vigila cambios que nunca llegan. Astro parte de la posición contraria a la de los frameworks de cliente: nada es interactivo salvo que tú lo pidas, en lugar de que todo lo sea aunque no lo necesites.
---// intento ingenuo de un contador dentro de un .astrolet contador = 0;function sumar() { contador++; }---<button>Suma</button><p>{contador}</p>Este código compila, pero el botón nunca cambiará el número. La función sumar no se conecta a ningún evento del navegador, y contador se evaluó una sola vez en el build valiendo cero, dejando el p impreso con ese cero para siempre. No hay motor de cliente que observe la variable ni repinte el elemento tras un clic. Para que ese contador cobre vida, su lógica debe mudarse a un componente de framework hidratado: la valla no es el lugar de la interactividad.
Cuándo cada uno
La regla práctica es económica: usa .astro por defecto y reserva un componente de framework para las islas que de verdad necesitan comportarse en el navegador. El coste de un .astro para el cliente es cero; el de una isla hidratada es el JavaScript de su framework más su propio código. Cada isla se justifica por sí misma.
En la práctica, la cabecera, el pie, la maquetación, las tarjetas, los artículos y el noventa por ciento del sitio son .astro. El carrusel con arrastre, el buscador con autocompletado, el carrito que actualiza un total, el formulario con validación en vivo son componentes de framework. La frontera no la marca la complejidad visual, sino si la pieza necesita responder al usuario después de cargarse.
Una heurística fiable para empezar: escríbelo todo en .astro y espera a sentir la falta de interactividad. Cuando un elemento pida de verdad responder a un clic, un tecleo o un gesto, extrae solo esa pieza a un componente de framework. Así el JavaScript de tu sitio crece por necesidad demostrada, nunca por hábito heredado.
Islas: el punto de encuentro
El puente entre ambos mundos son las directivas client:*. Colocas un componente de framework en una plantilla .astro y le añades una directiva que le dice a Astro cuándo hidratarlo —enviar su JavaScript y activarlo—. Sin directiva, ese componente es HTML estático; con ella, se convierte en una isla: una región interactiva rodeada de HTML inerte.
---import Cabecera from '../components/Cabecera.astro'; // .astro estaticoimport Buscador from '../components/Buscador.jsx'; // isla de framework---<Cabecera titulo="Panel" /><Buscador client:load /><footer>Contenido estático, cero JavaScript</footer>Las directivas gradúan cuándo ocurre la hidratación: client:load activa la isla en cuanto carga la página; client:idle espera a que el navegador esté ocioso; client:visible la difiere hasta que la isla entra en pantalla. Esa granularidad te deja pagar el coste de JavaScript solo cuando y donde aporta valor, en lugar de hidratar la página entera de golpe.
---import Menu from '../components/Menu.astro';import Carrusel from '../components/Carrusel.svelte';import Chat from '../components/Chat.tsx';---<Menu /><Carrusel client:visible /><Chat client:idle />Cada isla elige su framework y su momento de hidratación de forma independiente: aquí un carrusel de Svelte que despierta al entrar en pantalla convive con un chat de React que espera a que el navegador quede ocioso, todo alrededor de un menú .astro que jamás envía JavaScript. Ese es el modelo de islas condensado en una sola pantalla. La isla recibe props desde su anfitrión .astro como cualquier componente, pero con una salvedad: esos valores se serializan para cruzar del servidor al cliente, así que deben ser datos simples —cadenas, números, objetos planos—, nunca funciones ni instancias complejas.
En conjunto, estas directivas convierten la vieja pregunta binaria —sitio estático o aplicación— en un dial continuo. Cada componente elige su punto: cero JavaScript, hidratación diferida, hidratación inmediata o montaje solo en el cliente. Astro no te obliga a una postura global; te entrega un mando por isla y te pide usarlo con criterio.