Next.js s’est imposé comme l’un des frameworks React les plus adoptés pour bâtir des frontends headless, notamment grâce à son système de génération de pages statiques au build. Combiné à l’API REST WordPress, il permet de construire un blog rapide, bien référencé, tout en conservant WordPress comme back-office de contenu familier pour les équipes éditoriales.
Ce tutoriel construit, étape par étape, un blog headless minimal avec le Pages Router de Next.js : la liste des articles sur la page d’accueil, une page de détail par article générée dynamiquement, et une gestion de base du référencement.
Structure du projet
Un projet Next.js classique organise ses pages dans le dossier pages/. Pour notre blog headless, deux fichiers suffisent à démarrer : pages/index.js pour la liste des articles, et pages/articles/[slug].js pour la page de détail, où [slug] signale à Next.js une route dynamique.
Récupérer les articles au build avec getStaticProps
La fonction getStaticProps, exécutée côté serveur au moment du build, permet de récupérer les données avant même que la page ne soit générée en HTML statique :
// pages/index.js
export async function getStaticProps() {
const reponse = await fetch(
'https://exemple.fr/wp-json/wp/v2/posts?_embed&per_page=10'
);
const articles = await reponse.json();
return {
props: { articles },
revalidate: 60,
};
}
export default function Accueil( { articles } ) {
return (
<ul>
{articles.map( ( article ) => (
<li key={article.id}>
<a href={`/articles/${article.slug}`}>{article.title.rendered}</a>
</li>
) )}
</ul>
);
}
L’option revalidate: 60 active l’ISR (Incremental Static Regeneration), disponible depuis Next.js 9.5 : la page reste statique, mais Next.js la régénère automatiquement en arrière-plan si une requête arrive plus de soixante secondes après la dernière génération. Nous y reviendrons plus en détail dans un prochain article dédié à l’ISR combinée aux webhooks WordPress.

Générer les pages dynamiques avec getStaticPaths
Pour une route dynamique comme [slug].js, Next.js a besoin de savoir à l’avance quelles pages générer. C’est le rôle de getStaticPaths, qui liste tous les slugs possibles :
// pages/articles/[slug].js
export async function getStaticPaths() {
const reponse = await fetch(
'https://exemple.fr/wp-json/wp/v2/posts?_fields=slug&per_page=100'
);
const articles = await reponse.json();
return {
paths: articles.map( ( article ) => ( {
params: { slug: article.slug },
} ) ),
fallback: 'blocking',
};
}
export async function getStaticProps( { params } ) {
const reponse = await fetch(
`https://exemple.fr/wp-json/wp/v2/posts?slug=${params.slug}&_embed`
);
const [ article ] = await reponse.json();
if ( ! article ) {
return { notFound: true };
}
return { props: { article }, revalidate: 60 };
}
L’option fallback: 'blocking' mérite une attention particulière : elle permet de générer à la volée une page qui n’existait pas encore au build (un article publié après le dernier build), sans afficher de page d’erreur ni d’état de chargement intermédiaire.
Afficher le contenu de l’article
Le contenu HTML renvoyé par l’API REST WordPress doit être injecté avec précaution. Next.js/React n’interprète pas du HTML brut par défaut : il faut utiliser dangerouslySetInnerHTML, en toute conscience que le contenu provient d’une source de confiance (votre propre WordPress) :
export default function Article( { article } ) {
return (
<article>
<h1>{article.title.rendered}</h1>
<div
dangerouslySetInnerHTML={{ __html: article.content.rendered }}
/>
</article>
);
}
Un minimum de SEO avec next/head
Pour un blog, le référencement reste un critère central. Le composant Head de Next.js permet d’injecter les balises meta essentielles depuis les données récupérées :
import Head from 'next/head';
export default function Article( { article } ) {
return (
<>
<Head>
<title>{article.title.rendered}</title>
<meta
name="description"
content={article.excerpt.rendered.replace( /<[^>]*>/g, '' )}
/>
</Head>
{/* contenu de l'article */}
</>
);
}
Sur un vrai projet, extrayez systématiquement la logique d’appel à l’API REST dans un module partagé plutôt que de dupliquer les
fetchdans chaque page. Cela facilite grandement la migration future vers WPGraphQL ou vers une autre source de données.
En résumé
Le Pages Router de Next.js, associé à getStaticProps et getStaticPaths, offre une base solide et éprouvée pour un blog headless WordPress rapide et bien référencé. L’ISR, activée par la simple option revalidate, permet déjà de garder le contenu relativement à jour sans reconstruire l’intégralité du site à chaque publication. Nous approfondirons cette mécanique, combinée à des webhooks WordPress pour une revalidation à la demande, dans un prochain article.