L’App Router de Next.js, stabilisé depuis plusieurs versions, généralise l’usage des React Server Components comme brique par défaut. Un test a été mené sur un projet vitrine à faible enjeu, un blog personnel d’un consultant en cybersécurité, pour évaluer ce que change concrètement cette approche quand la source de contenu est un WordPress headless consommé via son API REST classique, sans passer par un client GraphQL ni même par une bibliothèque de fetching dédiée.
L’intérêt du test tenait dans sa simplicité assumée : plutôt que d’ajouter une couche d’abstraction supplémentaire, l’idée était de voir jusqu’où un appel fetch() natif, directement dans un composant serveur, suffisait à construire une page WordPress headless fonctionnelle et raisonnablement performante.
Le composant serveur le plus simple possible
Un composant serveur asynchrone peut directement attendre le résultat d’un appel réseau, sans hook, sans état de chargement à gérer manuellement :
async function PageArticle({ params }) {
const { slug } = await params;
const res = await fetch(
`https://cms.exemple.dev/wp-json/wp/v2/posts?slug=${slug}&_embed`,
{ next: { revalidate: 3600 } }
);
const [article] = await res.json();
if (!article) {
notFound();
}
return (
<article>
<h1>{article.title.rendered}</h1>
<div dangerouslySetInnerHTML={{ __html: article.content.rendered }} />
</article>
);
}
Aucune bibliothèque tierce, aucun état de chargement affiché côté client puisque tout se résout côté serveur avant même l’envoi du HTML au navigateur. Le résultat visuel, pour ce type de page simple, est indiscernable d’une génération statique classique, à la différence que la donnée est récupérée à chaque requête (ou selon la fenêtre de revalidation définie), potentiellement plus fraîche qu’un export purement statique régénéré à intervalles fixes.
Le piège du cache implicite de fetch()
La première surprise du test est venue du comportement par défaut de fetch() dans l’écosystème Next.js : sans option next.revalidate ou cache: 'no-store' explicite, l’appel est mis en cache indéfiniment côté serveur de build, un comportement hérité du modèle de rendu statique historique de Next.js mais qui surprend fortement un développeur venant du monde React classique, habitué à ce qu’un appel réseau soit toujours frais par défaut.

Sur ce projet, un article corrigé dans WordPress n’apparaissait pas mis à jour côté front tant que la fenêtre de revalidation n’était pas explicitement précisée sur chaque appel, ce qui a nécessité une revue systématique de tous les appels fetch() du projet pour s’assurer qu’aucun ne conservait le comportement de cache par défaut sans intention explicite.
Ce qui reste impossible sans composant client
Un composant serveur ne peut, par construction, gérer aucune interaction directe avec le visiteur : pas de onClick, pas d’état local, pas de useEffect. Le bouton « j’aime cet article », qui écrit un compteur via l’API REST WordPress, a donc dû être isolé dans un composant client explicitement marqué par la directive 'use client', recevant en props les données déjà récupérées côté serveur mais gérant lui-même l’appel réseau de mise à jour :
'use client';
function BoutonJaime({ postId, compteurInitial }) {
const [compteur, setCompteur] = useState(compteurInitial);
async function handleClick() {
setCompteur((c) => c + 1);
await fetch(`/api/jaime`, {
method: 'POST',
body: JSON.stringify({ postId }),
});
}
return <button onClick={handleClick}>{compteur} ♥</button>;
}
Cette frontière explicite entre serveur et client, imposée par le modèle des Server Components, a demandé une discipline de découpage des composants plus stricte que sur l’ancien Pages Router, où cette distinction restait implicite et souvent floue.
Les limites rencontrées sur ce test
- Le contenu HTML brut renvoyé par
content.rendered, injecté viadangerouslySetInnerHTML, reste un contenu que React ne contrôle pas : aucune hydratation d’interactivité n’est possible directement dans ce HTML sans un traitement supplémentaire pour repérer et remplacer certains éléments par des composants React interactifs. - Les erreurs réseau vers l’API WordPress, en cas d’indisponibilité temporaire du back-office, se traduisent par une page entière en erreur côté Next.js, sans le filet de sécurité qu’offrirait un cache local déjà généré au préalable dans une approche statique classique.
- Le débogage des composants serveur reste plus délicat : les
console.logplacés dans un composant serveur n’apparaissent jamais dans la console du navigateur, uniquement dans les journaux du serveur de rendu, ce qui a désorienté l’équipe habituée au débogage client classique.
Les React Server Components simplifient réellement l’écriture d’un site headless simple ; ils déplacent surtout la complexité vers une discipline plus stricte de gestion du cache et de la frontière serveur/client, qu’il vaut mieux anticiper que découvrir en production.
Ce qu’on retient de ce premier test
Pour un site headless au contenu majoritairement statique et peu interactif, se passer d’un client GraphQL ou d’une bibliothèque de fetching dédiée en s’appuyant uniquement sur fetch() natif dans des composants serveur est parfaitement viable, et même plus simple à comprendre pour un développeur qui découvre le projet. La vigilance doit se porter presque entièrement sur la gestion explicite du cache, un piège suffisamment fréquent pour mériter une checklist de revue de code dédiée avant toute mise en production.