Astro para desarrolladores de WordPress

En esta página

Astro es un framework web para sitios centrados en contenido. Con EmDash, Astro sustituye tu tema de WordPress: se encarga del templating, el enrutado y el renderizado.

Esta guía enseña los fundamentos de Astro mapeándolos a conceptos de WordPress que ya entiendes.

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, por defecto genera HTML estático con cero JavaScript.

Cero JS salvo que lo añadas

WordPress carga jQuery y los scripts del tema automáticamente. Astro no envía nada al navegador a menos que lo añadas explícitamente.

Arquitectura basada en componentes

En lugar de etiquetas de plantilla e includes dispersos, construyes con componentes autocontenidos y componibles.

Enrutado basado en archivos

Sin reglas de reescritura ni 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

Un proyecto Astro típico:

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 Astro

Los archivos .astro son el equivalente en Astro de las plantillas PHP. Cada archivo tiene dos partes:

  1. Frontmatter (entre cercas ---) — Código del servidor, como el PHP al inicio de una plantilla
  2. Plantilla — HTML con expresiones, como el resto de una plantilla PHP
---
// 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 respecto a PHP:

  • El frontmatter está aislado. Las variables declaradas ahí están disponibles en la plantilla, pero el código nunca llega al navegador.
  • Los imports van en el frontmatter. Componentes, datos, utilidades—todo importado arriba.
  • TypeScript funciona. Define tipos de props con interface Props para autocompletado y validación en el editor.

Expresiones de plantilla

Las plantillas 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

PatternPropó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 mediante props (como argumentos de función) y slots (como puntos de inserción de 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>

Uso:

<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 frente a $args

En WordPress, get_template_part() pasa datos mediante el array $args. Las props de Astro están tipadas y se obtienen por destructuring:

---
// Type-safe with defaults
interface Props {
  title: string;
  count?: number;
}
const { title, count = 10 } = Astro.props;
---

Slots frente a hooks

WordPress usa do_action() para crear puntos de inserción. Astro usa slots:

WordPressAstro
do_action('before_content')<slot name="before" />
Default content area<slot />
do_action('after_content')<slot name="after" />

La diferencia: los slots reciben elementos hijos en el sitio de la llamada, mientras que los hooks de WordPress requieren llamadas add_action() separadas en otro lugar.

Layouts

Los layouts envuelven páginas con HTML común—el <head>, cabecera, pie y todo lo compartido entre páginas. Sustituye header.php + footer.php.

---
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>

Usar 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 estilo. El más distintivo son los estilos con alcance (scoped).

Estilos con alcance

Los estilos en una etiqueta <style> quedan automáticamente limitados 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 evitar fugas de estilo. Se acabaron las guerras de especificidad.

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 sustituye construir strings de clases a mano:

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 cliente

Astro no envía JavaScript por defecto. Este es el mayor cambio mental respecto a WordPress.

Añadir interactividad

Para interacciones sencillas, añade 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 deduplican automáticamente. Si el componente aparece dos veces en la página, el script se ejecuta una vez.

Avanzado: componentes interactivos

Para interactividad más compleja, Astro puede cargar componentes JavaScript (React, Vue, Svelte) bajo demanda. Es opcional: la mayoría de sitios funcionan bien solo con <script>.

---
import SearchWidget from "../components/SearchWidget.jsx";
---
<!-- Only load JavaScript when the search box scrolls into view -->
<SearchWidget client:visible />
DirectiveCuándo carga JavaScript
client:loadAl cargar la página
client:visibleCuando el componente entra en el viewport
client:idleCuando el navegador está inactivo

Enrutado

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

FileURL
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].astroAny path (catch-all)

Rutas dinámicas

Para contenido CMS, usa corchetes en 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
Template hierarchy (single-post.php)Explicit file: posts/[slug].astro
Rewrite rules + query_varsFile structure
$wp_query determines templateURL maps directly to file
add_rewrite_rule()Create files or folders

Dónde viven los conceptos de WordPress

Referencia para encontrar el equivalente Astro/EmDash de funciones de WordPress:

Templating

WordPressAstro/EmDash
Template hierarchyFile-based routing in src/pages/
get_template_part()Import and use components
the_content()<PortableText value={content} />
the_title(), the_*()Access via post.data.title
Template tagsTemplate expressions {value}
body_class()class:list directive

Datos y consultas

WordPressAstro/EmDash
WP_QuerygetEmDashCollection(type, filters)
get_post()getEmDashEntry(type, id)
get_posts()getEmDashCollection(type)
get_the_terms()Access via entry.data.categories
get_post_meta()Access via entry.data.fieldName
get_option()getSiteSettings()
wp_nav_menu()getMenu(location)

Extensibilidad

WordPressAstro/EmDash
add_action()EmDash hooks, Astro middleware
add_filter()EmDash hooks
add_shortcode()Portable Text custom blocks
register_block_type()Portable Text custom blocks
register_sidebar()EmDash widget areas
PluginsAstro integrations + EmDash plugins

Tipos de contenido

WordPressAstro/EmDash
register_post_type()Create collection in admin UI
register_taxonomy()Create taxonomy in admin UI
register_meta()Add field to collection schema
Post statusEntry status (draft, published, etc.)
Featured imageMedia reference field
Gutenberg blocksPortable Text blocks

Resumen

El salto de WordPress a Astro es grande pero lógico:

  1. Plantillas PHP → componentes Astro — Misma idea (código servidor + HTML), mejor organización
  2. Etiquetas de plantilla → props e imports — Flujo de datos explícito en lugar de globales
  3. Archivos del tema → directorio pages — Las URLs siguen la estructura de archivos
  4. Hooks → slots y middleware — Puntos de inserción más predecibles
  5. jQuery por defecto → cero JS por defecto — Añade interactividad a propósito

Empieza con la guía Getting Started para tu primer sitio EmDash, o explora Working with Content para consultar y renderizar datos del CMS.