Astro para desarrolladores de WordPress

En esta página

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:

WordPressAstroPropósito
index.php, single.phpsrc/pages/Rutas (URLs)
template-parts/src/components/Piezas de UI reutilizables
header.php + footer.phpsrc/layouts/Envoltorios de página
style.csssrc/styles/CSS global
functions.phpastro.config.mjsConfiguració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:

  1. Frontmatter (entre delimitadores ---) — Código del lado del servidor, como PHP al inicio de una plantilla
  2. 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 Props para 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ónPropó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:

WordPressAstro
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>&copy; {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 />
DirectivaCuándo se carga JavaScript
client:loadInmediatamente al cargar la página
client:visibleCuando el componente entra en viewport
client:idleCuando el navegador está inactivo

Enrutamiento

Astro usa enrutamiento basado en archivos. Los archivos en src/pages/ se convierten en URLs:

ArchivoURL
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].astroCualquier 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

WordPressAstro
Jerarquía de plantillas (single-post.php)Archivo explícito: posts/[slug].astro
Reglas de reescritura + query_varsEstructura de archivos
$wp_query determina plantillaURL 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

WordPressAstro/EmDash
Jerarquía de plantillasEnrutamiento 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 plantillaExpresiones de plantilla {value}
body_class()Directiva class:list

Datos y consultas

WordPressAstro/EmDash
WP_QuerygetEmDashCollection(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

WordPressAstro/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
PluginsIntegraciones de Astro + plugins de EmDash

Tipos de contenido

WordPressAstro/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 postEstado de entrada (draft, published, etc.)
Imagen destacadaCampo de referencia de medios
Bloques de GutenbergBloques 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.