Integración con JavaScript y frameworks
Astro está diseñado para generar HTML rápido y ligero, pero también puedes añadir componentes interactivos (botones, sliders, contadores, etc.) usando JavaScript o frameworks conocidos.
1. Cómo añadir JavaScript en Astro #
Astro te da varias formas de usar JS.
Veamos las principales 👇
Opción 1. Script directo dentro del .astro #
Puedes escribir un <script> directamente en el mismo archivo.
1<h1>Contador simple</h1> 2<button id="btn">Haz clic</button> 3<p id="out">0</p> 4 5<script> 6 let contador = 0; 7 document.getElementById("btn").addEventListener("click", () => { 8 contador++; 9 document.getElementById("out").textContent = contador; 10 }); 11</script>
💡 Este código sí se envía al navegador (a diferencia del que va entre ---).
🟢 Úsalo para pequeñas interacciones.
Opción 2. Archivo JS externo #
También puedes enlazar un script separado:
1<html> 2 <head> 3 <script src="/scripts/main.js" defer></script> 4 </head> 5 <body> 6 <button id="btn">Incrementar</button> 7 <p id="out">0</p> 8 </body> 9</html>
public/scripts/main.js
1let contador = 0; 2document.getElementById("btn").addEventListener("click", () => { 3 contador++; 4 document.getElementById("out").textContent = contador; 5});
✅ Perfecto para organizar mejor tu código.
✅ Se guarda en public/ para que se sirva directamente.
Laboratorio: botones añadidos después de registrar el script #
Si tu interfaz añade tarjetas dinámicamente, los listeners registrados en los
botones iniciales no se copian a los nuevos. Experimenta con la delegación en el
contenedor y pulsa también el span interior para entender por qué se usa
closest() en lugar de asumir que target siempre es el botón.
Laboratorio interactivo
Delegación: un listener para elementos nuevos
Añade tarjetas después de registrar los listeners. Compara listeners directos con un listener en el contenedor que identifica el botón mediante closest().
2 botones · 0 listeners registrados · última selección: ninguna
contenedor.querySelectorAll("button[data-item]")
.forEach(boton => boton.addEventListener("click", seleccionar));
// Los botones añadidos después no tienen este listener.La delegación requiere que el evento llegue al contenedor. Si otro listener corta su propagación, este listener de burbujeo no lo recibirá. Cambiar el modo elimina los listeners anteriores.
Experimenta: Añade la tarjeta 3 en modo directo y pulsa su etiqueta: no se registra. Vuelve a enlazar o cambia a delegación. Añade otra y pulsa su span interior.
2. Diferencia entre JavaScript del servidor y del cliente #
Astro ejecuta dos tipos de código JS:
| Tipo | Dónde se ejecuta | Ejemplo |
|---|---|---|
| Servidor | En la parte superior del .astro (entre ---) | Lógica que genera contenido antes de enviarlo |
| Cliente | En <script> o componentes con client: | Interactividad en el navegador |
Ejemplo:
1--- 2const fecha = new Date().toLocaleDateString(); 3--- 4 5<p>Hoy es {fecha}</p>
El cálculo se hace en el servidor (no en el navegador).
3. Qué es la "Island Architecture" (arquitectura de islas) #
Astro genera una página estática (solo HTML)
y luego inyecta pequeñas “islas” interactivas solo donde las necesitas.
Por ejemplo:
- Una web estática con un contador React.
- Un blog estático con buscador dinámico en JS.
Esto ahorra rendimiento y JavaScript innecesario.
💨 Resultado: páginas ultrarrápidas.
4. Integrar frameworks (React, Vue, Svelte, ...) #
Astro te permite mezclar frameworks sin esfuerzo.
Por ejemplo, puedes usar React solo en un componente.
Paso 1. Instalar la integración #
Ejemplo con React:
1npx astro add react
Para Vue:
1npx astro add vue
Para Svelte:
1npx astro add svelte
Astro configurará todo automáticamente.
Paso 2. Crear un componente React #
src/components/Contador.jsx
1import { useState } from 'react'; 2 3export default function Contador() { 4 const [n, setN] = useState(0); 5 return ( 6 <div> 7 <h3>Contador React: {n}</h3> 8 <button onClick={() => setN(n + 1)}>Incrementar</button> 9 </div> 10 ); 11}
Paso 3. Usarlo dentro de una página .astro #
Laboratorio: actualizar el estado de una isla React #
El contador anterior captura el estado de su render. Compara tres llamadas en
un evento y comprueba por qué un actualizador funcional permite acumularlas.
Laboratorio interactivo
Renderizado y cola de estado
Ejecuta tres setState reales dentro de un mismo evento. El manejador conserva el estado capturado en su render; los actualizadores funcionales recorren la cola.
Estado en el render actual0
Ejecuta el evento para ver la cola.
La traza reconstruye el evento ya ejecutado; no cuenta renders internos ni representa trabajo concurrente o comprobaciones adicionales de Strict Mode.
Experimenta: Compara tres reemplazos con tres incrementos funcionales. En la cola mixta, observa cómo el reemplazo final con 42 descarta el resultado previo.
1--- 2import Contador from '../components/Contador.jsx'; 3--- 4 5<h1>Interactividad con React</h1> 6 7<!-- Se renderiza solo en el cliente --> 8<Contador client:load />
💡 El atributo client:load indica cuándo se carga el componente.
Vamos a ver los diferentes modos 👇
5. Modos de renderizado del cliente (client:) #
Astro ofrece varias estrategias para cargar componentes en el navegador.
| Modo | Cuándo se ejecuta | Ideal para |
|---|---|---|
client:load | Justo al cargar la página | Elementos esenciales (ej. botones) |
client:idle | Cuando el navegador está libre | Scripts no urgentes |
client:visible | Cuando el componente entra en pantalla | Elementos fuera del viewport |
client:media="(prefers-color-scheme: dark)" | Según una media query | Modo oscuro, responsive |
client:only="react" | Solo React en cliente, sin SSR | Apps 100% SPA |
Ejemplo:
1<Contador client:visible />
👉 El contador solo se activa cuando el usuario lo ve, ahorrando recursos.
6. Integrar librerías JS (ejemplo: Chart.js) #
Puedes usar cualquier librería JS en un componente de framework.
Ejemplo con React y Chart.js:
1npm install chart.js
src/components/Grafico.jsx
1import { useEffect, useRef } from 'react'; 2import { Chart } from 'chart.js/auto'; 3 4export default function Grafico() { 5 const canvasRef = useRef(); 6 7 useEffect(() => { 8 new Chart(canvasRef.current, { 9 type: 'bar', 10 data: { 11 labels: ['Ene', 'Feb', 'Mar'], 12 datasets: [{ label: 'Ventas', data: [10, 20, 15], backgroundColor: 'purple' }], 13 }, 14 }); 15 }, []); 16 17 return <canvas ref={canvasRef}></canvas>; 18}
Y en la página:
1<Grafico client:load />
🎉 Ahora tienes un gráfico interactivo dentro de Astro.
7. Cuándo usar frameworks (y cuándo no) #
✅ Usa Astro puro (sin frameworks) cuando:
- Tu sitio es informativo (blog, landing, portfolio).
- Solo necesitas HTML estático y algo de JS ligero.
✅ Usa React/Vue/Svelte cuando:
- Necesitas interactividad compleja (formularios, dashboards, animaciones).
- Quieres aprovechar librerías de esos ecosistemas.
Astro no te obliga a casarte con uno.
Puedes usar varios a la vez, o ninguno.
Resumen del tema
Conceptos clave #
- Arquitectura de Islas (
Islands Architecture): estrategia de renderizado que entrega HTML estático puro embebiendo componentes interactivos aislados e independientes. - Ejecución dual de JavaScript:
- Servidor / Build (
---): procesa datos y genera HTML sin transferir dependencias al cliente. - Cliente (
<script>): manipula el DOM e interactúa directamente en el navegador.
- Servidor / Build (
- Directivas de hidratación parcial (
client:*):client:load: hidrata el componente de forma síncrona al cargar la página.client:idle: retrasa la hidratación hasta que el hilo principal del navegador esté inactivo.client:visible: hidrata el componente solo cuando entra en el viewport del usuario.client:media="(query)": hidrata condicionado a una media query CSS.client:only="framework": omite el renderizado previo en el servidor y ejecuta únicamente en el cliente.
- Frameworks agnósticos: coexistencia de componentes de React, Vue o Svelte en un mismo proyecto mediante
npx astro add <framework>.
Qué debes recordar #
Aplica directivas de hidratación (client:visible, client:idle, client:load) únicamente a las islas interactivas que lo requieran para mantener el bundle JavaScript en mínimos.