Astro es un framework web para crear sitios web enfocados en contenido. Al usar EmDash, Astro reemplaza tu tema de WordPress: maneja el templating, el enrutamiento y el renderizado.
Esta guía enseña los fundamentos de Astro mapeándolos a conceptos de WordPress que ya comprendes.
Cambios de paradigma clave
Renderizado en servidor por defecto
Como PHP, el código de Astro se ejecuta en el servidor. A diferencia de PHP, genera HTML estático por defecto con cero JavaScript.
Cero JS a menos que lo agregues
WordPress carga jQuery y scripts del tema automáticamente. Astro no envía nada al navegador a menos que lo agregues explícitamente.
Arquitectura basada en componentes
En lugar de etiquetas de plantilla dispersas e includes, construye con componentes componibles y autónomos.
Enrutamiento basado en archivos
Sin reglas de reescritura o query_vars. La estructura de archivos en src/pages/ define tus URLs directamente.
Estructura del proyecto
Los temas de WordPress tienen una estructura plana con nombres de archivo mágicos. Astro usa directorios explícitos:
| WordPress | Astro | Propósito |
|---|---|---|
index.php, single.php | src/pages/ | Rutas (URLs) |
template-parts/ | src/components/ | Piezas de UI reutilizables |
header.php + footer.php | src/layouts/ | Envoltorios de página |
style.css | src/styles/ | CSS global |
functions.php | astro.config.mjs | Configuración del sitio |
El siguiente árbol muestra un diseño típico de proyecto Astro:
src/
├── components/ # Reusable UI (Header, PostCard, etc.)
├── layouts/ # Page shells (Base.astro)
├── pages/ # Routes - files become URLs
│ ├── index.astro # → /
│ ├── posts/
│ │ ├── index.astro # → /posts
│ │ └── [slug].astro # → /posts/hello-world
│ └── [slug].astro # → /about, /contact, etc.
└── styles/
└── global.css
Componentes de Astro
Los archivos .astro son el equivalente de Astro a las plantillas PHP. Cada archivo tiene dos partes:
- Frontmatter (entre delimitadores
---) — Código del lado del servidor, como PHP al inicio de una plantilla - Template — HTML con expresiones, como el resto de una plantilla PHP
El siguiente componente declara props tipados en el frontmatter y los renderiza en la plantilla:
---
// Frontmatter: runs on server, never sent to browser
interface Props {
title: string;
excerpt: string;
url: string;
}
const { title, excerpt, url } = Astro.props;
---
<!-- Template: outputs HTML -->
<article class="post-card">
<h2><a href={url}>{title}</a></h2>
<p>{excerpt}</p>
</article>
Diferencias clave con PHP:
- El frontmatter está aislado. Las variables declaradas allí están disponibles en la plantilla, pero el código en sí nunca llega al navegador.
- Los imports van en el frontmatter. Componentes, datos, utilidades: todo se importa en la parte superior.
- TypeScript funciona. Define tipos de props con
interface Propspara autocompletado del editor y validación.
Expresiones de plantilla
Las plantillas de Astro usan {llaves} en lugar de etiquetas <?php ?>. La sintaxis es similar a JSX pero genera HTML puro.
Astro
---
import { getEmDashCollection } from "emdash";
const { entries: posts } = await getEmDashCollection("posts");
const showTitle = true;
---
{showTitle && <h1>Latest Posts</h1>}
{posts.length > 0 ? (
<ul>
{posts.map(post => (
<li>
<a href={`/posts/${post.id}`}>{post.data.title}</a>
</li>
))}
</ul>
) : (
<p>No posts found.</p>
)} PHP
<?php
$posts = new WP_Query(['post_type' => 'post']);
$show_title = true;
?>
<?php if ($show_title): ?>
<h1>Latest Posts</h1>
<?php endif; ?>
<?php if ($posts->have_posts()): ?>
<ul>
<?php while ($posts->have_posts()): $posts->the_post(); ?>
<li>
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</li>
<?php endwhile; wp_reset_postdata(); ?>
</ul>
<?php else: ?>
<p>No posts found.</p>
<?php endif; ?> Patrones de expresión
| Patrón | Propósito |
|---|---|
{variable} | Mostrar un valor |
{condition && <Element />} | Renderizado condicional |
{condition ? <A /> : <B />} | If/else |
{items.map(item => <Li>{item}</Li>)} | Bucles |
Props y Slots
Los componentes reciben datos a través de props (como argumentos de función) y slots (como puntos de inserción do_action).
Astro
---
interface Props {
title: string;
featured?: boolean;
}
const { title, featured = false } = Astro.props;
---
<article class:list={["card", { featured }]}>
<h2>{title}</h2>
<slot />
<slot name="footer" />
</article>El siguiente marcado usa ese componente, pasando el slot predeterminado y un slot footer nombrado:
<Card title="Hello" featured>
<p>This goes in the default slot.</p>
<footer slot="footer">Footer content</footer>
</Card> PHP
<?php
// Usage: get_template_part('template-parts/card', null, [
// 'title' => 'Hello',
// 'featured' => true
// ]);
$title = $args['title'] ?? '';
$featured = $args['featured'] ?? false;
$class = $featured ? 'card featured' : 'card';
?>
<article class="<?php echo esc_attr($class); ?>">
<h2><?php echo esc_html($title); ?></h2>
<?php
// No direct equivalent to slots.
// WordPress uses do_action() for similar patterns:
do_action('card_content');
do_action('card_footer');
?>
</article> Props vs $args
En WordPress, get_template_part() pasa datos a través del array $args. Los props de Astro están tipados y desestructurados:
---
// Type-safe with defaults
interface Props {
title: string;
count?: number;
}
const { title, count = 10 } = Astro.props;
---
Slots vs Hooks
WordPress usa do_action() para crear puntos de inserción. Astro usa slots:
| WordPress | Astro |
|---|---|
do_action('before_content') | <slot name="before" /> |
| Área de contenido predeterminada | <slot /> |
do_action('after_content') | <slot name="after" /> |
La diferencia: los slots reciben elementos hijos en el sitio de llamada, mientras que los hooks de WordPress requieren llamadas add_action() separadas en otro lugar.
Layouts
Los layouts envuelven páginas con estructura HTML común: el <head>, encabezado, pie de página y todo lo compartido entre páginas. Esto reemplaza header.php + footer.php. El siguiente layout define esa estructura compartida y expone un slot para el contenido de la página:
---
import "../styles/global.css";
interface Props {
title: string;
description?: string;
}
const { title, description = "My EmDash Site" } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content={description} />
<title>{title}</title>
</head>
<body>
<header>
<nav><!-- Navigation --></nav>
</header>
<main>
<slot />
</main>
<footer>
<p>© {new Date().getFullYear()}</p>
</footer>
</body>
</html>
Usa el layout en una página:
---
import Base from "../layouts/Base.astro";
---
<Base title="Home">
<h1>Welcome</h1>
<p>Page content goes in the slot.</p>
</Base>
Estilos
Astro ofrece varios enfoques de estilización. El más distintivo son los estilos con alcance.
Estilos con alcance
Los estilos en una etiqueta <style> se limitan automáticamente a ese componente:
<article class="card">
<h2>Title</h2>
</article>
<style>
/* Only affects .card in THIS component */
.card {
padding: 1rem;
border: 1px solid #ddd;
}
h2 {
color: navy;
}
</style>
El HTML generado incluye nombres de clase únicos para prevenir la fuga de estilos, por lo que los estilos de los componentes permanecen contenidos sin escalar la especificidad del selector.
Estilos globales
Para estilos de todo el sitio, crea un archivo CSS e impórtalo en un layout:
---
import "../styles/global.css";
---
Clases condicionales
La directiva class:list reemplaza la construcción manual de strings de clase:
Astro
---
const { featured, size = "medium" } = Astro.props;
---
<article class:list={[
"card",
size,
{ featured, "has-border": true }
]}>Salida: <article class="card medium featured has-border">
PHP
<?php
$classes = ['card', $size];
if ($featured) $classes[] = 'featured';
if (true) $classes[] = 'has-border';
?>
<article class="<?php echo esc_attr(implode(' ', $classes)); ?>"> JavaScript del lado del cliente
Astro envía cero JavaScript por defecto. Este es el cambio mental más grande desde WordPress.
Agregar interactividad
Para interacciones simples, agrega una etiqueta <script>:
<button id="menu-toggle">Menu</button>
<nav id="mobile-menu" hidden>
<slot />
</nav>
<script>
const toggle = document.getElementById("menu-toggle");
const menu = document.getElementById("mobile-menu");
toggle?.addEventListener("click", () => {
menu?.toggleAttribute("hidden");
});
</script>
Los scripts se empaquetan y deduplicán automáticamente. Si este componente aparece dos veces en una página, el script se ejecuta una vez.
Componentes interactivos avanzados
Para interactividad más compleja, Astro puede cargar componentes JavaScript (React, Vue, Svelte) bajo demanda. Esto es opcional: la mayoría de los sitios funcionan bien solo con etiquetas <script>. La siguiente página carga un componente solo cuando se desplaza a la vista:
---
import SearchWidget from "../components/SearchWidget.jsx";
---
<!-- Only load JavaScript when the search box scrolls into view -->
<SearchWidget client:visible />
| Directiva | Cuándo se carga JavaScript |
|---|---|
client:load | Inmediatamente al cargar la página |
client:visible | Cuando el componente entra en viewport |
client:idle | Cuando el navegador está inactivo |
Enrutamiento
Astro usa enrutamiento basado en archivos. Los archivos en src/pages/ se convierten en URLs:
| Archivo | URL |
|---|---|
src/pages/index.astro | / |
src/pages/about.astro | /about |
src/pages/posts/index.astro | /posts |
src/pages/posts/[slug].astro | /posts/hello-world |
src/pages/[...slug].astro | Cualquier ruta (catch-all) |
Rutas dinámicas
Para contenido CMS, usa sintaxis de corchetes para segmentos dinámicos:
---
import { getEmDashCollection, getEmDashEntry } from "emdash";
import Base from "../../layouts/Base.astro";
import { PortableText } from "emdash/ui";
// For static builds, define which pages to generate
export async function getStaticPaths() {
const { entries: posts } = await getEmDashCollection("posts");
return posts.map(post => ({
params: { slug: post.id },
props: { post },
}));
}
const { post } = Astro.props;
---
<Base title={post.data.title}>
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>
</Base>
Comparado con WordPress
| WordPress | Astro |
|---|---|
Jerarquía de plantillas (single-post.php) | Archivo explícito: posts/[slug].astro |
Reglas de reescritura + query_vars | Estructura de archivos |
$wp_query determina plantilla | URL mapea directamente al archivo |
add_rewrite_rule() | Crear archivos o carpetas |
Dónde viven los conceptos de WordPress
Una referencia para encontrar el equivalente Astro/EmDash de las características de WordPress:
Templating
| WordPress | Astro/EmDash |
|---|---|
| Jerarquía de plantillas | Enrutamiento basado en archivos en src/pages/ |
get_template_part() | Importar y usar componentes |
the_content() | <PortableText value={content} /> |
the_title(), the_*() | Acceso vía post.data.title |
| Etiquetas de plantilla | Expresiones de plantilla {value} |
body_class() | Directiva class:list |
Datos y consultas
| WordPress | Astro/EmDash |
|---|---|
WP_Query | getEmDashCollection(type, filters) |
get_post() | getEmDashEntry(type, id) |
get_posts() | getEmDashCollection(type) |
get_the_terms() | Acceso vía entry.data.categories |
get_post_meta() | Acceso vía entry.data.fieldName |
get_option() | getSiteSettings() |
wp_nav_menu() | getMenu(location) |
Extensibilidad
| WordPress | Astro/EmDash |
|---|---|
add_action() | Hooks de EmDash, middleware de Astro |
add_filter() | Hooks de EmDash |
add_shortcode() | Bloques personalizados de Portable Text |
register_block_type() | Bloques personalizados de Portable Text |
register_sidebar() | Áreas de widgets de EmDash |
| Plugins | Integraciones de Astro + plugins de EmDash |
Tipos de contenido
| WordPress | Astro/EmDash |
|---|---|
register_post_type() | Crear colección en UI de admin |
register_taxonomy() | Crear taxonomía en UI de admin |
register_meta() | Agregar campo al esquema de colección |
| Estado de post | Estado de entrada (draft, published, etc.) |
| Imagen destacada | Campo de referencia de medios |
| Bloques de Gutenberg | Bloques de Portable Text |
Mapeo de conceptos
Los principales cambios de WordPress a Astro cubiertos en esta guía:
- Las plantillas PHP se convierten en componentes Astro: código del servidor más HTML, con organización de archivos explícita.
- Las etiquetas de plantilla se convierten en props e imports: los datos fluyen a través de argumentos en lugar de globales.
- Los archivos de tema se convierten en un directorio de páginas: las URLs coinciden con la estructura de archivos.
- Los hooks se convierten en slots y middleware: los puntos de inserción se definen donde se pasa el contenido.
- jQuery se carga por defecto en WordPress; Astro no envía JavaScript hasta que lo agregues.
Comienza con la guía Getting Started para crear tu primer sitio EmDash, o explora Working with Content para aprender cómo consultar y renderizar datos CMS.