Volver al blog
Astro Performance Web Development Islands
Imagen de portada del artículo: Astro: Construyendo Sitios Web Ultrarrápidos con Islands Architecture

Astro: Construyendo Sitios Web Ultrarrápidos con Islands Architecture

14 min
German

Astro ha revolucionado la forma de construir sitios web orientados a contenido. Su filosofía es radical: envía cero JavaScript al navegador por defecto. Solo añade JS donde realmente lo necesitas, a través del concepto de "islas de interactividad".

Este portfolio que estás leyendo está construido con Astro, y en este artículo te enseño cómo funciona todo.

¿Por qué Astro?

El problema de los SPAs para contenido

Si construyes un blog con React, Vue o Angular, el navegador tiene que:

  1. Descargar el HTML (casi vacío).
  2. Descargar y parsear React (100KB+).
  3. Descargar y ejecutar tu código.
  4. Renderizar el contenido.

¿Todo eso para mostrar un artículo de texto? Astro resuelve esto generando HTML estático en el servidor y solo enviando JavaScript donde sea necesario.

Comparativa de JavaScript enviado al navegador

Framework JS para un blog post
Next.js (React) ~80-150KB
Nuxt (Vue) ~60-120KB
SvelteKit ~30-60KB
Astro ~0KB (sin islas)

Estructura de un proyecto Astro

code
src/
├── components/     # Componentes .astro
│   ├── Header.astro
│   ├── Footer.astro
│   └── Card.astro
├── layouts/        # Layouts reutilizables
│   └── MainLayout.astro
├── pages/          # Rutas basadas en archivos
│   ├── index.astro        # → /
│   ├── about.astro        # → /about
│   └── blog/
│       ├── index.astro    # → /blog
│       └── [slug].astro   # → /blog/mi-post
├── styles/
│   └── global.css
public/              # Assets estáticos (sin procesar)

Componentes Astro: Lo básico

Un componente .astro tiene dos partes: un frontmatter (JavaScript del servidor) y un template (HTML).

astro
---
// Esto se ejecuta en BUILD TIME, no en el navegador
import Header from '../components/Header.astro';

interface Props {
  title: string;
  description: string;
}

const { title, description } = Astro.props;
const publishedDate = new Date().toISOString();

// Puedes hacer fetch a APIs aquí
const response = await fetch('https://api.example.com/data');
const data = await response.json();
---

<html lang="es">
  <head>
    <title>{title}</title>
    <meta name="description" content={description} />
  </head>
  <body>
    <Header />
    <h1>{title}</h1>
    <p>Publicado: {publishedDate}</p>
    <slot />  <!-- Equivalente a children en React -->
  </body>
</html>

Clave: Todo el JavaScript del frontmatter se ejecuta en el servidor durante el build. Cero JS llega al navegador.

Islands Architecture: Islas de Interactividad

La magia de Astro está en poder usar React, Vue, Svelte o Solid solo donde necesites interactividad:

astro
---
import StaticHeader from '../components/Header.astro';  // Sin JS
import ReactCounter from '../components/Counter.jsx';    // Con JS
import VueSearch from '../components/Search.vue';        // Con JS
---

<!-- El header es HTML puro, 0 JS -->
<StaticHeader />

<main>
  <h1>Mi página</h1>
  <p>Este contenido es HTML estático</p>
  
  <!-- Esta "isla" carga React solo para este componente -->
  <ReactCounter client:visible />
  
  <!-- Esta isla carga Vue solo cuando el usuario interactúa -->
  <VueSearch client:idle />
</main>

Directivas client:*

Directiva Cuándo se hidrata Caso de uso
client:load Inmediatamente Elementos above the fold interactivos
client:idle Cuando el navegador está idle Componentes no críticos
client:visible Cuando entra en viewport Componentes below the fold
client:media Cuando se cumple un media query Componentes responsive
client:only Solo cliente (sin SSR) Componentes que usan APIs del navegador

Content Collections: Contenido tipado

Astro tiene un sistema integrado para manejar colecciones de contenido (como posts de blog) con validación de esquema:

typescript
// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.date(),
    tags: z.array(z.string()),
    image: z.string().optional(),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog };

Luego creas archivos Markdown/MDX en src/content/blog/:

markdown
---
title: "Mi primer post"
description: "Una introducción a Astro"
pubDate: 2025-01-15
tags: ["astro", "web"]
---

# Mi primer post

Contenido del post aquí...

Y los consultas con tipos:

astro
---
import { getCollection } from 'astro:content';

const posts = await getCollection('blog', ({ data }) => !data.draft);
const sortedPosts = posts.sort((a, b) => b.data.pubDate - a.data.pubDate);
---

{sortedPosts.map(post => (
  <article>
    <h2><a href={`/blog/${post.slug}`}>{post.data.title}</a></h2>
    <time>{post.data.pubDate.toLocaleDateString()}</time>
  </article>
))}

View Transitions

Astro incluye View Transitions nativas que crean transiciones fluidas entre páginas sin necesidad de un SPA:

astro
---
import { ClientRouter } from 'astro:transitions';
---

<html>
  <head>
    <ClientRouter />
  </head>
  <body>
    <h1 transition:name="title">Mi Título</h1>
    <img transition:name="hero" src="/hero.jpg" />
  </body>
</html>

Al navegar entre páginas, los elementos con el mismo transition:name se animan suavemente de una posición a otra. Es la misma tecnología que usa este blog.

Conclusión

Astro es la elección perfecta cuando tu prioridad es rendimiento y contenido. No necesitas sacrificar la experiencia del desarrollador: puedes usar tu framework favorito como islas, tienes tipado con TypeScript, y las View Transitions dan una experiencia de SPA sin el coste de JavaScript. Si estás construyendo un blog, portfolio, documentación o landing page, Astro debería ser tu primera opción.