Astro est un framework web pour des sites centrés sur le contenu. Avec EmDash, Astro remplace votre thème WordPress—il gère le templating, le routage et le rendu.
Ce guide enseigne les fondamentaux d’Astro en les reliant aux concepts WordPress que vous maîtrisez déjà.
Changements de paradigme clés
Rendu serveur par défaut
Comme PHP, le code Astro s’exécute sur le serveur. Contrairement à PHP, il produit par défaut du HTML statique avec zéro JavaScript.
Zéro JS sauf si vous l'ajoutez
WordPress charge jQuery et les scripts du thème automatiquement. Astro n’envoie rien au navigateur sauf si vous l’ajoutez explicitement.
Architecture à composants
Au lieu de balises de modèle et d’inclusions éparpillées, vous construisez avec des composants autonomes et composables.
Routage basé sur les fichiers
Pas de règles de réécriture ni de query_vars. La structure des fichiers dans src/pages/ définit directement vos URL.
Structure du projet
Les thèmes WordPress ont une structure plate avec des noms de fichiers « magiques ». Astro utilise des dossiers explicites :
| WordPress | Astro | Rôle |
|---|---|---|
index.php, single.php | src/pages/ | Routes (URL) |
template-parts/ | src/components/ | Morceaux d’UI réutilisables |
header.php + footer.php | src/layouts/ | Enveloppes de page |
style.css | src/styles/ | CSS global |
functions.php | astro.config.mjs | Configuration du site |
Un projet Astro typique :
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
Composants Astro
Les fichiers .astro sont l’équivalent Astro des modèles PHP. Chaque fichier a deux parties :
- Frontmatter (entre les balises
---) — Code côté serveur, comme le PHP en tête de modèle - Modèle — HTML avec expressions, comme le reste d’un modèle 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>
Différences clés par rapport à PHP :
- Le frontmatter est isolé. Les variables y déclarées sont disponibles dans le modèle, mais le code n’atteint jamais le navigateur.
- Les imports vont dans le frontmatter. Composants, données, utilitaires—tout est importé en haut.
- TypeScript fonctionne. Définissez les types de props avec
interface Propspour l’autocomplétion et la validation dans l’éditeur.
Expressions de modèle
Les modèles Astro utilisent {accolades} à la place des balises <?php ?>. La syntaxe ressemble à JSX mais produit du HTML pur.
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; ?> Motifs d’expression
| Pattern | Objectif |
|---|---|
{variable} | Afficher une valeur |
{condition && <Element />} | Rendu conditionnel |
{condition ? <A /> : <B />} | If/else |
{items.map(item => <Li>{item}</Li>)} | Boucles |
Props et slots
Les composants reçoivent des données via des props (comme des arguments de fonction) et des slots (comme des points d’insertion 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>Utilisation :
<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
Dans WordPress, get_template_part() passe des données via le tableau $args. Les props Astro sont typées et destructurées :
---
// Type-safe with defaults
interface Props {
title: string;
count?: number;
}
const { title, count = 10 } = Astro.props;
---
Slots vs hooks
WordPress utilise do_action() pour créer des points d’insertion. Astro utilise des slots :
| WordPress | Astro |
|---|---|
do_action('before_content') | <slot name="before" /> |
| Default content area | <slot /> |
do_action('after_content') | <slot name="after" /> |
La différence : les slots reçoivent des éléments enfants à l’appel, tandis que les hooks WordPress exigent des appels add_action() séparés ailleurs.
Layouts
Les layouts enveloppent les pages avec une structure HTML commune—le <head>, l’en-tête, le pied de page et tout ce qui est partagé. Cela remplace 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>© {new Date().getFullYear()}</p>
</footer>
</body>
</html>
Utiliser le layout dans une page :
---
import Base from "../layouts/Base.astro";
---
<Base title="Home">
<h1>Welcome</h1>
<p>Page content goes in the slot.</p>
</Base>
Styles
Astro propose plusieurs approches de style. La plus distinctive est le style scoped.
Styles scoped
Les styles dans une balise <style> sont automatiquement limités à ce composant :
<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>
Le HTML généré inclut des noms de classe uniques pour éviter les fuites de style. Fini les guerres de spécificité.
Styles globaux
Pour des styles sur tout le site, créez un fichier CSS et importez-le dans un layout :
---
import "../styles/global.css";
---
Classes conditionnelles
La directive class:list remplace la construction manuelle de chaînes de classes :
Astro
---
const { featured, size = "medium" } = Astro.props;
---
<article class:list={[
"card",
size,
{ featured, "has-border": true }
]}>Sortie : <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 côté client
Astro n’envoie aucun JavaScript par défaut. C’est le plus grand changement mental par rapport à WordPress.
Ajouter de l’interactivité
Pour des interactions simples, ajoutez une balise <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>
Les scripts sont regroupés et dédupliqués automatiquement. Si le composant apparaît deux fois sur une page, le script s’exécute une fois.
Avancé : composants interactifs
Pour une interactivité plus complexe, Astro peut charger des composants JavaScript (React, Vue, Svelte) à la demande. C’est optionnel—la plupart des sites se contentent de balises <script>.
---
import SearchWidget from "../components/SearchWidget.jsx";
---
<!-- Only load JavaScript when the search box scrolls into view -->
<SearchWidget client:visible />
| Directive | Moment du chargement JS |
|---|---|
client:load | Immédiatement au chargement |
client:visible | Quand le composant entre dans la fenêtre |
client:idle | Quand le navigateur est inactif |
Routage
Astro utilise un routage basé sur les fichiers. Les fichiers dans src/pages/ deviennent des URL :
| File | 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 | Any path (catch-all) |
Routes dynamiques
Pour le contenu CMS, utilisez la syntaxe entre crochets pour les segments dynamiques :
---
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>
Comparé à WordPress
| WordPress | Astro |
|---|---|
Template hierarchy (single-post.php) | Explicit file: posts/[slug].astro |
Rewrite rules + query_vars | File structure |
$wp_query determines template | URL maps directly to file |
add_rewrite_rule() | Create files or folders |
Où vivent les concepts WordPress
Référence pour trouver l’équivalent Astro/EmDash des fonctionnalités WordPress :
Templating
| WordPress | Astro/EmDash |
|---|---|
| Template hierarchy | File-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 tags | Template expressions {value} |
body_class() | class:list directive |
Données et requêtes
| WordPress | Astro/EmDash |
|---|---|
WP_Query | getEmDashCollection(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) |
Extensibilité
| WordPress | Astro/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 |
| Plugins | Astro integrations + EmDash plugins |
Types de contenu
| WordPress | Astro/EmDash |
|---|---|
register_post_type() | Create collection in admin UI |
register_taxonomy() | Create taxonomy in admin UI |
register_meta() | Add field to collection schema |
| Post status | Entry status (draft, published, etc.) |
| Featured image | Media reference field |
| Gutenberg blocks | Portable Text blocks |
Résumé
Le passage de WordPress à Astro est important mais logique :
- Modèles PHP → composants Astro — Même idée (code serveur + HTML), meilleure organisation
- Balises de modèle → props et imports — Flux de données explicite plutôt que des globales
- Fichiers de thème → dossier pages — Les URL suivent la structure des fichiers
- Hooks → slots et middleware — Points d’insertion plus prévisibles
- jQuery par défaut → zéro JS par défaut — Ajoutez l’interactivité volontairement
Commencez par le guide Getting Started pour votre premier site EmDash, ou explorez Working with Content pour interroger et rendre les données du CMS.