Astro

Framework web enfocado en contenido - cero JS por defecto, arquitectura de islas, React/Vue/Svelte juntos

TL;DR

En una línea: Astro es el framework web para sitios orientados al contenido - no envía JavaScript por defecto, hidrata solo lo que necesita ser interactivo.

Fortalezas principales:

  • Cero JS por defecto - HTML estático, puntuaciones Lighthouse de 100
  • Arquitectura de islas - hidrata solo componentes interactivos
  • Usa cualquier framework - React, Vue, Svelte, Solid en el mismo proyecto
  • Content Collections - Markdown/MDX type-safe con validación

Core Concepts

Concept 1: Islands Architecture

Los componentes son estáticos por defecto. Añade interactividad selectivamente:

---
import ReactCounter from './Counter.jsx';
import VueModal from './Modal.vue';
---

<!-- Estático - cero JS enviado -->
<h1>Mi Blog</h1>
<p>Este contenido es HTML puro</p>

<!-- Islas - JS solo para estos -->
<ReactCounter client:visible />  <!-- Hidratar cuando sea visible -->
<VueModal client:load />         <!-- Hidratar al cargar la página -->
<ReactCounter client:idle />     <!-- Hidratar cuando el navegador esté inactivo -->

Concept 2: .astro Components

Renderizado en servidor por defecto. Frontmatter para lógica, template para marcado:

---
// Se ejecuta en tiempo de build (lado servidor)
const response = await fetch('https://api.example.com/posts');
const posts = await response.json();
---

<ul>
  {posts.map(post => (
    <li><a href={`/blog/${post.slug}`}>{post.title}</a></li>
  ))}
</ul>

<style>
  /* Alcance limitado a este componente */
  li { margin-bottom: 0.5rem; }
</style>

Concept 3: Content Collections

Contenido type-safe con validación Zod:

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

const blog = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    date: z.date(),
    tags: z.array(z.string()).optional(),
  }),
});

export const collections = { blog };
---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
---

Quick Start

Create Project

npm create astro@latest my-blog
cd my-blog

Project Structure

src/
├── pages/          # Enrutamiento basado en archivos
│   ├── index.astro     → /
│   ├── about.astro     → /about
│   └── blog/[slug].astro → /blog/:slug
├── components/     # Componentes reutilizables
├── layouts/        # Layouts de página
└── content/        # Content Collections

Run

npm run dev
# Abre http://localhost:4321

Gotchas

File-based routing rules

src/pages/
├── index.astro           → /
├── about.astro           → /about
├── blog/index.astro      → /blog
├── blog/[slug].astro     → /blog/my-post
└── [...slug].astro       → /any/nested/path (catch-all)

Choosing the right client directive

<!-- Más común - cargar cuando sea visible en el viewport -->
<Component client:visible />

<!-- Interactividad crítica - cargar inmediatamente -->
<Component client:load />

<!-- No crítico - cargar cuando el navegador esté inactivo -->
<Component client:idle />

<!-- Solo en una media query específica -->
<Component client:media="(max-width: 768px)" />

Dynamic routes need getStaticPaths

---
// src/pages/blog/[slug].astro
export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map(post => ({
    params: { slug: post.slug },
    props: { post },
  }));
}

const { post } = Astro.props;
---

When to Use

Ideal para:

  • Blogs y sitios de documentación
  • Páginas de marketing/landing
  • Sitios web con mucho contenido
  • Sitios donde el rendimiento es crítico

No ideal para:

  • Apps altamente interactivas (dashboards, SPAs)
  • Aplicaciones en tiempo real
  • Apps que necesitan enrutamiento del lado del cliente

Comparación:

CaracterísticaAstroNext.jsGatsby
JS por defectoCeroReact completoReact completo
Lock-in de frameworkNingunoReactReact
Velocidad de buildRápidaMediaLenta
Caso de usoContenidoApps + ContenidoContenido

Next Steps

Cheatsheet

PatrónCódigo
Componente estático<Component />
Hidratar cuando visible<Component client:visible />
Hidratar al cargar<Component client:load />
Hidratar cuando inactivo<Component client:idle />
Añadir integraciónnpx astro add react
Buildnpm run build
Previsualizarnpm run preview