Cheat Sheet completa de Astro


Esta cheat sheet de Astro reúne los conceptos y patrones más usados del framework: desde la estructura inicial y el enrutado, hasta islands (hidratación parcial), endpoints, Content Collections, MDX e integraciones populares (React, Tailwind, etc.). Ideal para construir sitios rápidos, accesibles y mantenibles.

Estructura mínima del proyecto

.
├─ public/                  # archivos estáticos (pasan tal cual)
├─ src/
│  ├─ pages/               # .astro / .md / .mdx → rutas
│  ├─ layouts/             # plantillas reutilizables
│  ├─ components/          # componentes .astro o de frameworks
│  ├─ content/             # Content Collections (markdown/mdx)
│  ├─ styles/              # CSS global/modular
│  └─ env.d.ts             # tipos para import.meta.env y content
├─ astro.config.mjs        # configuración de Astro
├─ tsconfig.json           # (si usas TS)
└─ package.json

Páginas y routing

---
// src/pages/index.astro  →  /
const title = "Home";
---
<html lang="es">
  <head><title>{title}</title></head>
  <body>
    <h1>Hola Astro</h1>
  </body>
</html>
---
// src/pages/blog/[slug].astro  →  /blog/mi-post
export async function getStaticPaths() {
  const posts = [{ slug: "mi-post" }, { slug: "otro-post" }];
  return posts.map((p) => ({ params: p }));
}
const { slug } = Astro.params;
---
<h1>Entrada: {slug}</h1>

Rutas útiles

  • src/pages/about.astro/about
  • src/pages/blog/index.astro/blog
  • src/pages/[...404].astro → página 404 personalizada

Layouts y slots

---
// src/layouts/BaseLayout.astro
const { title = "Sitio" } = Astro.props;
---
<html lang="es">
  <head>
    <meta charset="utf-8" />
    <title>{title}</title>
  </head>
  <body>
    <header>Mi header</header>
    <main><slot /></main>
    <footer><slot name="footer" /></footer>
  </body>
</html>
---
// src/pages/ejemplo.astro
import Base from "../layouts/BaseLayout.astro";
---
<Base title="Página ejemplo">
  <h1>Contenido</h1>
  <div slot="footer">© {new Date().getFullYear()}</div>
</Base>

Componentes .astro: script, estilos y props

---
// src/components/Card.astro
export interface Props {
  title: string;
  href?: string;
}
const { title, href = "#" } = Astro.props;
---
<article class="card">
  <h3><a href={href}>{title}</a></h3>
  <slot />
</article>

<style>
  .card { padding: 1rem; border-radius: 12px; background: var(--backPage); }
  .card a { text-decoration: none; }
</style>

Bloques en .astro

  • Frontmatter (---) corre en el servidor (build/SSR).
  • Marcado es HTML + JSX-like.
  • <style> y <script> son scoped por defecto; usa is:global para global.

Framework components + Islands (hidratación parcial)

---
// src/pages/islands.astro
import Counter from "../components/Counter.jsx"; // React
---
<h2>Isla React</h2>
<Counter client:idle />      <!-- hidrata cuando el hilo está libre -->
<Counter client:visible />   <!-- al entrar en viewport -->
<Counter client:load />      <!-- inmediatamente -->
<Counter client:only="react" /> <!-- solo cliente, no SSR -->

Directivas client:* más comunes

  • client:load · client:idle · client:visible
  • client:media="(min-width: 768px)"
  • client:only="react|vue|svelte|preact|solid"

Patrones: hidrata solo lo interactivo; el resto, HTML estático ultra rápido.


Endpoints (API routes)

// src/pages/api/time.ts
export const GET = () => {
  return new Response(JSON.stringify({ now: Date.now() }), {
    headers: { "Content-Type": "application/json" },
  });
};

export const POST: APIRoute = async ({ request }) => {
  const data = await request.json();
  return new Response(JSON.stringify({ ok: true, data }));
};
  • Soporta GET/POST/PUT/DELETE/OPTIONS y APIRoute (TypeScript).
  • Útiles para formularios, webhooks, proxys a APIs, etc.

Fetch de datos (build o SSR)

---
// build-time: se ejecuta en el servidor durante el build
const res = await fetch("https://api.example.com/posts");
const posts = await res.json();
---
<ul>
  {posts.map((p) => <li>{p.title}</li>)}
</ul>

SSR (habilitar en astro.config.mjs o por página con export const prerender = false).


Content Collections (Markdown/MDX tipadas)

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

const blog = defineCollection({
  type: "content",
  schema: z.object({
    title: z.string(),
    pubDate: z.string(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog };
<!-- src/content/blog/mi-post.md -->
---
title: "Mi post"
pubDate: "2025-10-28"
tags: ["astro","mdx"]
---
Contenido **Markdown**.
---
// src/pages/blog/[...slug].astro
import { getCollection } from "astro:content";
export async function getStaticPaths() {
  const posts = await getCollection("blog", ({ data }) => !data.draft);
  return posts.map((p) => ({ params: { slug: p.slug }, props: p }));
}
const post = Astro.props;
---
<article>
  <h1>{post.data.title}</h1>
  <post.Content />
</article>

Ventajas: esquema tipado, filtros por draft, getEntryBySlug, etc.


MDX: componentes en Markdown

---
title: "MDX con componentes"
---

import Card from "../components/Card.astro";

<Card title="Hola desde MDX">
  MDX te permite usar componentes **.astro** o de frameworks.
</Card>

Añade la integración @astrojs/mdx en astro.config.mjs.


Imágenes y assets

---
import { Image } from "astro:assets";
import portada from "../images/cover.png";
---
<Image src={portada} alt="Cover" widths={[320, 640, 960]} sizes="(min-width: 768px) 640px, 100vw" />
  • Usa astro:assets para optimización (formatos, tamaños).
  • Archivos en public/ se sirven sin procesar.

Integraciones frecuentes

// astro.config.mjs
import { defineConfig } from "astro/config";
import mdx from "@astrojs/mdx";
import react from "@astrojs/react";
import tailwind from "@astrojs/tailwind";

export default defineConfig({
  integrations: [mdx(), react(), tailwind()],
});
  • Otras: @astrojs/sitemap, @astrojs/image, @astrojs/vercel/adapter

SSR, SSG y prerender

// astro.config.mjs
export default {
  output: "static", // SSG (default) | "server" para SSR
};
// Forzar por página
export const prerender = true;   // generar estático
// o
export const prerender = false;  // requiere SSR/adapter

Guía rápida

  • Blog/Docs: SSG + prerender
  • Dashboards o auth: SSR + adapter ✅

Adapters y despliegue

// Ej. Vercel
import vercel from "@astrojs/vercel/server";
export default { adapter: vercel() };

Otros: Netlify, Cloudflare, Node, Static (por defecto).


Variables de entorno

// .env
PUBLIC_API_BASE=/api          # visible en cliente (prefijo PUBLIC_)
SECRET_KEY=supersecreto       # solo servidor

// uso
const base = import.meta.env.PUBLIC_API_BASE;

Recuerda declarar tipos en src/env.d.ts si usas TS.


Redirecciones y headers

// astro.config.mjs
export default {
  redirects: {
    "/old": "/new",
    "/docs/*": "/guides/:splat",
  },
};

Para headers avanzados, usa el sistema del proveedor (Vercel/Netlify) o un middleware SSR.


Formularios (sin JS y con JS)

---
// Acción al endpoint interno
---
<form method="POST" action="/api/contact">
  <input name="email" type="email" required />
  <button>Enviar</button>
</form>
// src/pages/api/contact.ts
export const POST = async ({ request }) => {
  const data = await request.formData();
  const email = data.get("email");
  return new Response(null, { status: 303, headers: { Location: "/gracias" } });
};

Comandos útiles (CLI)

# Crear proyecto
npm create astro@latest

# Dev / Build / Preview
npm run dev
npm run build
npm run preview

# Añadir integración
npx astro add react

Patrones comunes

Listados desde Content Collections

---
import { getCollection } from "astro:content";
const posts = await getCollection("blog", ({ data }) => !data.draft);
---
<ul>
  {posts.map(({ id, data, slug }) => (
    <li><a href={`/blog/${slug}`}>{data.title}</a></li>
  ))}
</ul>

Island con estado (React)

// src/components/Counter.jsx
import { useState } from "react";
export default function Counter() {
  const [n, setN] = useState(0);
  return <button onClick={() => setN(n + 1)}>Clicks: {n}</button>;
}
---
import Counter from "../components/Counter.jsx";
---
<Counter client:visible />

Metadatos por página

---
Astro.props; // también puedes exportar "const prerender" o "const trailingSlash"
---
<head>
  <title>Mi título</title>
  <meta name="description" content="Descripción SEO" />
</head>

Rendimiento y buenas prácticas

  • Prefiere HTML estático y usa islands solo donde haya interacción.
  • Carga diferida: client:visible, client:idle, client:media.
  • Usa Image de astro:assets y tamaños adecuados.
  • Divide contenido largo con Content Collections + paginación.
  • Respeta accesibilidad (semántica, alt, aria-*).
  • Activa compresión/edge en tu plataforma de despliegue.

Snippets rápidos

Paginación estática

// src/pages/blog/index.astro (ejemplo)
export async function getStaticPaths() {
  const perPage = 6;
  const posts = await getCollection("blog");
  const totalPages = Math.ceil(posts.length / perPage);
  return Array.from({ length: totalPages }, (_, i) => ({
    params: { page: String(i + 1) },
    props: { page: i + 1, perPage, posts },
  }));
}

Guard SSR (solo usuarios logados, ejemplo genérico)

// src/middleware.ts (SSR requerido)
import type { MiddlewareHandler } from "astro";
export const onRequest: MiddlewareHandler = async (ctx, next) => {
  const isAuthed = Boolean(ctx.cookies.get("token")?.value);
  if (!isAuthed && ctx.url.pathname.startsWith("/dashboard")) {
    return ctx.redirect("/login");
  }
  return next();
};

Usamos cookies para mejorar tu experiencia. ¿Aceptas las cookies de análisis?