Opérations Astro
Faits vérifiés en date de juillet 2026.
Modèles complets pour le développement avec le framework Astro : architecture en îlots, collections de contenu, stratégies de rendu, transitions de vue et déploiement multi-plateforme.
Arbre de décision pour la stratégie de rendu
Quelle stratégie de rendu ?
│
├─ Le contenu est-il principalement statique (blog, docs, marketing) ?
│ ├─ OUI → Change-t-il moins d'une fois par jour ?
│ │ ├─ OUI → SSG (output: 'static')
│ │ │ TTFB le plus rapide, compatible CDN, coût d'exécution nul
│ │ └─ NON → Hybride (output: 'hybrid')
│ │ Par défaut statique + SSR par route
│ └─ NON → Chaque page a-t-elle besoin de personnalisation ?
│ ├─ OUI → SSR (output: 'server')
│ │ Dynamique par requête, conscient de l'authentification, données en temps réel
│ └─ NON → Hybride (output: 'hybrid')
│ Coquille statique + îlots serveur pour les parties dynamiques
│
├─ L'application a-t-elle besoin d'interactivité en temps réel (tableau de bord, SPA) ?
│ ├─ OUI → Est-ce une SPA complète avec routage côté client ?
│ │ ├─ OUI → Envisager une SPA React/Vue, ou Astro + client:only
│ │ └─ NON → Hybride + architecture en îlots
│ │ Îlots interactifs dans des pages statiques
│ └─ NON → SSG (output: 'static')
│
├─ Problèmes de temps de construction (>10 000 pages) ?
│ ├─ OUI → Hybride avec rendu à la demande
│ │ Prérendu des pages populaires, SSR pour la longue traîne
│ └─ NON → SSG gère bien
│
└─ Besoin d'edge computing (faible latence globalement) ?
├─ OUI → SSR + adaptateur Cloudflare/Vercel Edge
└─ NON → SSR + adaptateur Node ou SSG
Configuration
// astro.config.mjs
import { defineConfig } from 'astro/config';
// SSG (par défaut) - toutes les pages pré-rendues à la construction
export default defineConfig({
output: 'static',
});
// SSR - toutes les pages rendues sur demande
export default defineConfig({
output: 'server',
adapter: cloudflare(), // ou vercel(), netlify(), node()
});
// Hybride - statique par défaut, SSR par page
export default defineConfig({
output: 'hybrid',
adapter: cloudflare(),
});
---
// En mode hybride, désactiver le pré-rendu pour des pages spécifiques :
export const prerender = false;
// En mode SSR, activer le pré-rendu :
export const prerender = true;
---
Référence rapide de l'architecture en îlots
| Directive | Hydrate Quand | JS Envoyé | Cas d'utilisation |
|---|---|---|---|
client:load | Immédiatement au chargement de la page | Bundle complet | Interactivité au-dessus de la ligne de flottaison (navigation, CTA du héros) |
client:idle | Une fois la page inactive (requestIdleCallback) | Bundle complet | Interactivité en dessous de la ligne de flottaison (formulaire de commentaire, chat) |
client:visible | Lorsque défilé dans la zone visible | Bundle complet | Éléments en bas de page (widget de pied, carrousel) |
client:media | Lorsque la requête média correspond | Bundle complet | Navigation mobile uniquement, composants responsifs |
client:only="react" | Immédiatement, saute complètement le SSR | Bundle complet | Composants qui ne peuvent pas utiliser SSR (canvas, WebGL) |
| (aucune) | Jamais - HTML statique uniquement | Zéro JS | Contenu statique, cartes, en-têtes |
---
import NavBar from '../components/NavBar.tsx';
import CommentForm from '../components/CommentForm.tsx';
import ImageCarousel from '../components/ImageCarousel.svelte';
import MobileMenu from '../components/MobileMenu.vue';
import ThreeScene from '../components/ThreeScene.tsx';
---
<!-- Charge immédiatement - interactivité critique -->
<NavBar client:load />
<!-- Charge une fois la page inactive - non critique -->
<CommentForm client:idle />
<!-- Charge lorsque défilé dans la zone visible - paresseux -->
<ImageCarousel client:visible />
<!-- Charge uniquement sur mobile -->
<MobileMenu client:media="(max-width: 768px)" />
<!-- Uniquement côté client, pas de SSR (WebGL ne peut pas s'exécuter sur serveur) -->
<ThreeScene client:only="react" />
Démarrage rapide des collections de contenu
Définir le schéma
// src/content.config.ts (Astro 5) ou src/content/config.ts (Astro 4)
import { defineCollection, z, reference } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
schema: z.object({
title: z.string(),
description: z.string().max(160),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
heroImage: z.string().optional(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
author: reference('authors'), // Référence à une autre collection
}),
});
const authors = defineCollection({
loader: glob({ pattern: '**/*.json', base: './src/content/authors' }),
schema: z.object({
name: z.string(),
avatar: z.string(),
bio: z.string(),
socials: z.object({
twitter: z.string().optional(),
github: z.string().optional(),
}).optional(),
}),
});
export const collections = { blog, authors };
Interroger les collections
---
import { getCollection, getEntry } from 'astro:content';
// Obtenir tous les articles de blog non-brouillons, triés par date
const posts = (await getCollection('blog', ({ data }) => !data.draft))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
// Obtenir une seule entrée
const post = await getEntry('blog', 'my-first-post');
// Résoudre une référence
const author = await getEntry(post.data.author);
// Rendre le contenu
const { Content, headings } = await post.render();
---
<Content />
Collections de contenu vs CMS externe
| Critère | Collections de contenu | CMS externe (Payload, etc.) |
|---|---|---|
| Type de contenu | Markdown/MDX local, docs, blogs | Modèles de données relationnels |
| Auteurs | Développeurs (versionné) | Éditeurs (admin UI, authentification multi-utilisateur) |
| Validation | Type-safe via Zod à la construction | Schémas côté CMS + contrats API |
| Fréquence de mise à jour | Déployé avec le site | Indépendant des déploiements |
| Besoin d'API | Aucun (requêtes à la construction) | REST/GraphQL pour d'autres consommateurs |
| Flux de travail | PRs Git, révision simple | Flux éditoriaux, brouillons, rôles |
Règle générale : commencez avec les Collections de contenu ; optez pour un CMS uniquement lorsque des non-développeurs doivent publier sans déploiement, ou lorsque le contenu est véritablement relationnel.
Référence de la structure du projet
project-root/
├── astro.config.mjs # Configuration Astro
├── tsconfig.json # Configuration TypeScript (étend astro/tsconfigs)
├── package.json
├── public/ # Actifs statiques (copiés tels quels)
│ ├── favicon.svg
│ ├── robots.txt
│ └── og-image.png
├── src/
│ ├── pages/ # Routage basé sur les fichiers
│ │ ├── index.astro # → /
│ │ ├── about.astro # → /about
│ │ ├── blog/
│ │ │ ├── index.astro # → /blog
│ │ │ └── [slug].astro # → /blog/:slug (dynamique)
│ │ ├── api/
│ │ │ └── search.ts # → /api/search (point de terminaison API)
│ │ └── [...slug].astro # → attrape-tout/404
│ ├── layouts/
│ │ ├── BaseLayout.astro # Coquille HTML, <head>, styles globaux
│ │ └── BlogPost.astro # Mise en page d'article de blog
│ ├── components/
│ │ ├── Header.astro # Composant Astro statique
│ │ ├── Footer.astro
│ │ ├── NavBar.tsx # Îlot React
│ │ └── Counter.svelte # Îlot Svelte
│ ├── content/ # Fichiers sources des collections de contenu
│ │ ├── blog/
│ │ │ ├── post-one.md
│ │ │ └── post-two.mdx
│ │ └── authors/
│ │ └── jane.json
│ ├── content.config.ts # Schémas de collection (Astro 5)
│ ├── middleware.ts # Middleware requête/réponse
│ ├── styles/
│ │ └── global.css
│ └── lib/ # Utilitaires partagés
│ ├── utils.ts
│ └── constants.ts
└── .env # Variables d'environnement
Référence rapide des transitions de vue
---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---
<html>
<head>
<ViewTransitions />
</head>
<body>
<slot />
</body>
</html>
Directives de transition
<!-- Conserver l'élément entre les pages (conserve l'état, évite le re-rendu) -->
<audio transition:persist id="player">
<source src="/music.mp3" />
</audio>
<!-- Transition nommée pour l'appairage d'animation -->
<img transition:name="hero" src={post.heroImage} />
<!-- Animation personnalisée -->
<div transition:animate="slide">Contenu</div>
<div transition:animate="fade">Contenu</div>
<div transition:animate="none">Pas d'animation</div>
<!-- Persister avec un nom (pour plusieurs éléments persistants) -->
<video transition:persist="media-player" />
Événements du cycle de vie
<script>
document.addEventListener('astro:before-preparation', (e) => {
// Avant que la nouvelle page ne soit récupérée - annuler la navigation, afficher le chargement
});
document.addEventListener('astro:after-preparation', (e) => {
// Nouvelle page récupérée, avant l'échange
});
document.addEventListener('astro:before-swap', (e) => {
// Personnaliser le comportement de l'échange DOM
});
document.addEventListener('astro:after-swap', () => {
// DOM mis à jour - réinitialiser les scripts
});
document.addEventListener('astro:page-load', () => {
// Page entièrement chargée (se déclenche au premier chargement et à chaque navigation)
// Utilisez ceci au lieu de DOMContentLoaded avec View Transitions
});
</script>
Gestion avant/arrière
// astro.config.mjs
export default defineConfig({
prefetch: {
prefetchAll: true, // Précharger tous les liens au survol
defaultStrategy: 'hover', // 'hover' | 'tap' | 'viewport' | 'load'
},
});
<!-- Contrôle de préchargement par lien -->
<a href="/about" data-astro-prefetch>Précharger au survol (par défaut)</a>
<a href="/blog" data-astro-prefetch="viewport">Précharger lorsqu'il est visible</a>
<a href="/contact" data-astro-prefetch="load">Précharger immédiatement</a>
<a href="/external" data-astro-prefetch="false">Pas de préchargement</a>
Arbre de décision pour le déploiement
Où déployer ?
│
├─ Besoin d'edge computing + écosystème Cloudflare (KV, D1, R2) ?
│ └─ Cloudflare Pages/Workers
│ Adaptateur : @astrojs/cloudflare
│ Idéal pour : Edge global, liaisons Workers, économique
│
├─ Besoin de serverless + écosystème Vercel (ISR, analytics) ?
│ └─ Vercel
│ Adaptateur : @astrojs/vercel
│ Idéal pour : Migration Next.js, optimisation d'images, ISR
│
├─ Besoin de serverless + écosystème Netlify (formulaires, identité) ?
│ └─ Netlify
│ Adaptateur : @astrojs/netlify
│ Idéal pour : JAMstack, formulaires intégrés, tests fractionnés
│
├─ Besoin de contrôle total du serveur (Docker, runtime personnalisé) ?
│ └─ Node.js (autonome ou Express/Fastify)
│ Adaptateur : @astrojs/node
│ Idéal pour : Auto-hébergé, WebSocket, processus longs
│
└─ Site purement statique (pas besoin de SSR) ?
└─ N'importe quel hébergeur statique (GitHub Pages, S3, Cloudflare Pages)
Pas d'adaptateur nécessaire, output: 'static'
Idéal pour : Blogs, docs, sites marketing
Installation des adaptateurs
# Cloudflare
npx astro add cloudflare
# Vercel
npx astro add vercel
# Netlify
npx astro add netlify
# Node.js
npx astro add node
Pièges courants
| Piège | Pourquoi | Correctif |
|---|---|---|
| Erreurs d'hydratation | HTML serveur différent du rendu client (dates, ID aléatoires, API navigateur) | Utiliser client:only pour les composants dépendant du navigateur, ou assurer un rendu déterministe |
import.meta.env indéfini côté client | Seules les variables préfixées par PUBLIC_ sont exposées au code côté client | Renommer en PUBLIC_MY_VAR ou passer via les props depuis le serveur |
| Routes dynamiques 404 en SSG | getStaticPaths() ne retourne pas tous les paramètres possibles | Assurer que getStaticPaths() retourne chaque chemin valide, ou passer en hybride/SSR |
| Images non optimisées | Utilisation de <img> au lieu du composant <Image /> d'Astro | Importer depuis astro:assets : import { Image } from 'astro:assets' et utiliser des imports locaux pour src |
| SSR échoue sans adaptateur | output: 'server' ou 'hybrid' nécessite un adaptateur de déploiement | Installer l'adaptateur : npx astro add cloudflare (ou vercel, netlify, node) |
| Composants MDX non rendus | Composants personnalisés non passés au contenu MDX | Passer les composants via <Content components={{ MyComponent }} /> ou utiliser la config MDX dans astro.config.mjs |
| Modifications du schéma de collection non reflétées | La génération de types est mise en cache, les types .astro sont obsolètes | Exécuter astro sync pour régénérer les types, redémarrer le serveur de développement |
client:* sur les composants Astro | Les directives client fonctionnent uniquement sur les composants framework (React, Vue, Svelte) | Les composants Astro sont uniquement statiques; extraire les parties interactives vers un composant framework |
document / window non défini | Le code côté serveur ne peut pas accéder aux globaux du navigateur | Protéger avec if (typeof window !== 'undefined') ou déplacer dans client:only |
| Fuites de styles entre composants | Utilisation de CSS global au lieu de styles scopés | Utiliser <style> (scopé par défaut dans .astro) ou <style is:global> intentionnellement |
| View Transitions interrompt les scripts | DOMContentLoaded ne se déclenche qu'une fois avec View Transitions | Utiliser l'événement astro:page-load, qui se déclenche à chaque navigation |
| Variables d'environnement manquantes en production | .env non chargé ou variables de plateforme non configurées | Utiliser envField dans astro.config.mjs pour la validation; définir les variables dans le tableau de bord de la plateforme |
Checklist de sécurité pour la production
Pour chaque déploiement en production, traiter :
- En-têtes CSP - configurer une
Content-Security-Policyrestrictive (voir les motifs de middleware dansreferences/deployment.md) - Restrictions d'images distantes - appliquer des listes d'autorisation explicites
image.domains/remotePatterns; ne jamais dériver les URL d'images d'une entrée utilisateur (risque SSRF) - Validation de l'en-tête Host - vérifier que l'hôte de la requête correspond aux domaines attendus dans le middleware (SSR/hybride uniquement)
- Gestion des secrets - sur Cloudflare, utiliser les Workers Bindings (
wrangler secret put), pas de variables d'environnement intégrées dans le code ; ailleurs, utiliser les magasins de secrets de la plateforme - HTTPS uniquement - assurer que toutes les ressources externes (scripts, images, polices) sont chargées via HTTPS
- Validation des entrées - nettoyer toute entrée utilisateur dans les contextes SSR (paramètres de requête, corps de formulaire, cookies)
Fichiers de référence
| Fichier | Contenu | Lignes |
|---|---|---|
references/content-collections.md | Motifs de schéma, types Zod, interrogation, MDX, API de couche de contenu, migrations | ~500 |
references/islands-rendering.md | Motifs avancés d'îlots, interactive island, island map, API de rendu hybride, optimisation | ~350 |
references/view-transitions.md | Motifs de transition, défilement par ancre, router SPA, animations personnalisées, état partagé | ~300 |
references/deployment.md | Motifs de middleware, configuration CSP, proxy, multi-région, revalidation, rolling deploys | ~400 |
references/testing.md | Tests de rendu, tests d'îlots, tests e2e, habillage D1, tests de snapshot | ~250 |