# Next.js App Router et WordPress headless : ce qui change vraiment

> L'App Router de Next.js 13.4 rebat les cartes du headless WordPress : Server Components, fetch natif avec cache, generateMetadata. Voici comment migrer.

- Auteur : Clément Hadrot
- Publié le : 2023-08-17
- Mis à jour le : 2023-08-17
- Catégorie : Headless &amp; API
- URL : https://wpmoderne.dev.wordpress-developpement.fr/headless/nextjs-app-router-wordpress-headless/

## L’essentiel

- fetch natif remplace getStaticProps grâce au cache intégré
- generateMetadata gère le SEO sans composant Head
- Les Server Components suppriment le JavaScript inutile côté client

Depuis la version 13.4 de Next.js, sortie en mai 2023, l'App Router est officiellement considéré comme stable et recommandé pour les nouveaux projets. Pour un frontend WordPress headless, ce changement d'architecture n'est pas cosmétique : il transforme profondément la façon de récupérer les données, de gérer le cache et de construire les métadonnées SEO, par rapport au Pages Router que nous avions détaillé dans un article précédent.

Cet article passe en revue les changements concrets à connaître pour migrer, ou démarrer directement, un projet headless WordPress avec l'App Router.

## Server Components : la récupération de données change de logique

Sur le Pages Router, la récupération de données passait par des fonctions dédiées, `getStaticProps` ou `getServerSideProps`, séparées du composant lui-même. Avec l'App Router, les composants sont des *Server Components* par défaut, capables d'effectuer directement des appels asynchrones dans leur corps, sans fonction intermédiaire :

```
// app/articles/[slug]/page.js
export default async function PageArticle( { params } ) {
  const reponse = await fetch(
    `https://exemple.fr/wp-json/wp/v2/posts?slug=${params.slug}&_embed`,
    { next: { revalidate: 60 } }
  );
  const [ article ] = await reponse.json();

  return (
    <article>
      <h1>{article.title.rendered}</h1>
      <div dangerouslySetInnerHTML={{ __html: article.content.rendered }} />
    </article>
  );
}
```

Ce composant s'exécute entièrement côté serveur, et aucun JavaScript correspondant à sa logique de récupération n'est envoyé au navigateur, contrairement à un composant React classique. C'est un changement de philosophie important : par défaut, tout est Server Component, et il faut explicitement marquer un composant avec la directive `'use client'` pour obtenir de l'interactivité côté navigateur.

## Le cache intégré à fetch

La grande nouveauté réside dans l'objet `next` passé en option à `fetch`, qui remplace la logique de `revalidate` auparavant portée par `getStaticProps` :

```
// Équivalent d'une page statique classique, mise en cache indéfiniment
fetch( url, { cache: 'force-cache' } );

// Équivalent de l'ISR, régénérée toutes les 60 secondes
fetch( url, { next: { revalidate: 60 } } );

// Équivalent d'un rendu dynamique à chaque requête
fetch( url, { cache: 'no-store' } );
```

Ce système donne un contrôle beaucoup plus fin, requête par requête, sur la stratégie de cache, là où le Pages Router imposait une seule stratégie par page entière.

> L'essentiel à retenir : fetch natif remplace getStaticProps grâce au cache intégré ; generateMetadata gère le SEO sans composant Head ; Les Server Components suppriment le JavaScript inutile côté client

## generateMetadata remplace next/head

Pour le référencement, l'App Router introduit la fonction `generateMetadata`, exécutée côté serveur avant le rendu de la page, en remplacement du composant `Head` utilisé sur le Pages Router :

```
export async function generateMetadata( { params } ) {
  const reponse = await fetch(
    `https://exemple.fr/wp-json/wp/v2/posts?slug=${params.slug}`
  );
  const [ article ] = await reponse.json();

  return {
    title: article.title.rendered,
    description: article.excerpt.rendered.replace( /<[^>]*>/g, '' ),
    openGraph: {
      images: [ article._embedded?.[ 'wp:featuredmedia' ]?.[ 0 ]?.source_url ],
    },
  };
}
```

Cette approche évite un problème fréquent du Pages Router : le risque de dupliquer l'appel réseau entre le composant et le `Head`. Next.js déduplique automatiquement les appels `fetch` identiques exécutés pendant le rendu d'une même requête, y compris entre `generateMetadata` et le composant de page.

## Les routes API deviennent des Route Handlers

La route de revalidation par webhook, détaillée dans un précédent article avec le Pages Router, se réécrit sous forme de *Route Handler* :

```
// app/api/revalidate/route.js
import { revalidatePath } from 'next/cache';

export async function POST( request ) {
  const { secret, slug } = await request.json();

  if ( secret !== process.env.WEBHOOK_REVALIDATE_SECRET ) {
    return Response.json( { message: 'Secret invalide' }, { status: 401 } );
  }

  revalidatePath( `/articles/${ slug }` );
  revalidatePath( '/' );

  return Response.json( { revalidated: true } );
}
```

## Ce qu'il faut arbitrer avant de migrer

- Les composants interactifs existants doivent être audités et marqués `'use client'` explicitement, un oubli casse le composant silencieusement
- Le cache géré par `fetch` demande une vigilance nouvelle : une option de cache mal choisie peut servir du contenu obsolète sans erreur visible
- Certaines bibliothèques tierces, notamment celles qui dépendent d'API navigateur, restent incompatibles avec les Server Components et nécessitent un composant client englobant

> Sur une migration récente, le point qui a demandé le plus d'attention n'était pas la récupération de données, plutôt intuitive, mais la revue systématique de chaque composant pour décider s'il devait rester Server Component ou basculer en Client Component. C'est ce découpage qui détermine la quantité de JavaScript réellement envoyée au navigateur.

## En résumé

L'App Router change en profondeur la manière de construire un frontend WordPress headless avec Next.js : récupération de données directement dans les Server Components, cache géré finement via `fetch`, métadonnées SEO générées côté serveur avec `generateMetadata`. Le gain principal reste la réduction du JavaScript envoyé au client, au prix d'une courbe d'apprentissage réelle sur la distinction entre composants serveur et composants client.
