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→/aboutsrc/pages/blog/index.astro→/blogsrc/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; usais:globalpara 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:visibleclient: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/OPTIONSyAPIRoute(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/mdxenastro.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:assetspara 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.tssi 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
Imagedeastro:assetsy 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();
};