WebEngine

Next.js

Server Components Next.js : ce qui change vraiment pour un projet

Server Components Next.js : ce qui change vraiment pour un projet en production — bundle JS, data fetching, SEO et Core Web Vitals, sans jargon inutile.

NEXT.JS web-engine.fr

Un projet Next.js qui charge 400 Ko de JavaScript pour afficher une fiche produit statique, ce n’est pas une fatalité du framework, c’est une architecture mal pensée. Les Server Components, généralisés depuis l’App Router, changent la question qu’on se pose au moment de coder un composant : ce n’est plus « est-ce que ça marche côté client », c’est « est-ce que ça a besoin de tourner côté client ». Pour un projet en cours de conception ou de refonte, cette bascule a des conséquences concrètes sur le bundle, le temps de chargement, la facture d’hébergement et la façon dont l’équipe écrit le code au quotidien.

Ce que les Server Components changent dans l’architecture d’un projet

Avant l’App Router, un composant React s’exécutait quasi systématiquement des deux côtés : rendu initial sur le serveur pour le SSR, puis hydratation et ré-exécution côté navigateur pour devenir interactif. Chaque ligne de code finissait dans le bundle JavaScript envoyé au client, qu’elle serve à afficher du texte statique ou à gérer un clic.

Avec les Server Components, un composant par défaut ne s’exécute que sur le serveur. Il ne fait jamais partie du bundle client. Il peut accéder directement à une base de données, lire un fichier, appeler une API interne avec une clé secrète, sans passer par une route API intermédiaire. Le résultat HTML est envoyé au navigateur, point. Ce n’est un composant client, avec la directive 'use client' en tête de fichier, que s’il a besoin d’un state, d’un effet, d’un écouteur d’événement ou d’une API du navigateur.

Ça change la structure d’un projet dans le détail : les pages, les layouts, les listes, les fiches produit, les articles de blog restent des Server Components. Seuls les éléments réellement interactifs (formulaire, filtre, panier, carrousel) descendent en Client Components, en périphérie de l’arbre.

Zéro JS envoyé au client par défaut : impact réel sur le bundle

Sur un projet e-commerce ou éditorial classique, la majorité du contenu d’une page est en lecture seule. Un catalogue de produits, une page de blog, une fiche de contact : rien de tout ça n’a besoin de logique côté client. Avec le modèle pré-RSC, ou un App Router mal utilisé avec 'use client' posé en haut de l’arbre par réflexe, tout ce contenu embarque du JavaScript inutile.

Sur un projet migré correctement, le bundle JS envoyé pour une page produit peut passer de plusieurs centaines de Ko à quelques dizaines, le reste restant du HTML pur généré côté serveur. C’est ce delta qui fait basculer un score PageSpeed mobile de 60 à 90+, pas un réglage d’image ou un plugin de cache. Le lien direct se fait avec le Time to Interactive et le Total Blocking Time : moins de JS à parser et exécuter, moins de blocage du thread principal.

L’erreur la plus fréquente sur les projets qui migrent mal : poser 'use client' sur un composant racine parce qu’un seul enfant a besoin d’interactivité. Ça rend client tout le sous-arbre, et on perd l’essentiel du bénéfice. La bonne pratique est inverse : isoler le composant interactif au plus près de la feuille, et remonter les Server Components autour.

Data fetching direct dans le composant : la fin des waterfalls d’API

Avant, un composant qui avait besoin de données passait soit par getServerSideProps, soit par un appel useEffect côté client après montage — ce qui crée un aller-retour réseau supplémentaire après le premier rendu, donc un écran vide ou un skeleton avant que la vraie donnée arrive.

Avec les Server Components, on appelle directement une fonction async dans le composant :

// app/produits/[slug]/page.js — Server Component
export default async function ProduitPage({ params }) {
  const produit = await getProduit(params.slug);
  return (
    <div>
      <h1>{produit.nom}</h1>
      <p>{produit.prix} €</p>
      <AjouterAuPanier produitId={produit.id} />
    </div>
  );
}

AjouterAuPanier est le seul morceau client de la page, avec son propre 'use client'. Le reste ne fait aucun aller-retour réseau visible par l’utilisateur : la donnée est déjà présente dans le HTML servi. Sur des pages avec plusieurs sources de données (produit, avis, produits liés, stock), on peut aussi paralléliser les appels avec Promise.all côté serveur, plutôt que de chaîner des useEffect qui se déclenchent en cascade côté client.

Server vs Client Components : où tracer la frontière

La question à se poser composant par composant : a-t-il besoin de useState, useEffect, d’un événement DOM, du localStorage, ou d’une librairie qui dépend du navigateur, comme un slider ou une carte interactive ? Si non, il reste Server Component par défaut, sans rien à écrire. Si oui, il devient Client Component, mais on le garde le plus petit et le plus bas possible dans l’arbre.

Un point qui piège souvent les équipes qui migrent depuis Pages Router : dès qu’un fichier a 'use client', tout ce qu’il importe suit la même règle côté bundle, sauf si on le passe en tant que children ou prop depuis un parent serveur. C’est ce pattern de composition, pas l’import direct, qui permet de garder un layout serveur avec un widget client injecté dedans.

Streaming, Suspense et le nouveau modèle de chargement

Les Server Components s’articulent avec <Suspense> pour du streaming HTML : la page part au navigateur dès que les premières sections sont prêtes, les blocs plus lents (avis clients, recommandations qui dépendent d’un appel tiers) arrivent en flux après, sans bloquer le rendu initial. Ça change la métrique à optimiser : ce n’est plus « attendre que tout soit prêt avant d’envoyer », c’est « découper la page en sections qui arrivent dans l’ordre de priorité ».

Pour un projet avec un appel API lent, comme un ERP ou un connecteur de stock tiers, c’est souvent la différence entre un LCP à 4 secondes et un LCP à 1,2 seconde : le contenu principal (titre, image, prix) part immédiatement, le bloc lent est enveloppé dans son propre Suspense avec un fallback léger.

Impact sur le SEO et les Core Web Vitals

Google indexe le HTML rendu, streaming compris — les crawlers modernes attendent le rendu complet côté serveur avant d’évaluer le contenu, donc le SEO ne perd rien avec cette approche, contrairement à un rendu 100 % client où le contenu n’existe qu’après exécution du JS. C’est même l’inverse : moins de JS signifie un LCP et un INP plus bas, deux métriques qui font partie des Core Web Vitals utilisés par Google dans son évaluation de l’expérience de page.

Sur un projet Next.js dont l’objectif est un score PageSpeed mobile 90+, les Server Components ne sont pas une option parmi d’autres, c’est le levier principal. Un projet Next.js construit dès le départ avec cette architecture évite le travail de rattrapage qu’on voit sur les projets migrés à moitié, où certaines pages restent en Client Components lourds par manque de temps.

Les pièges classiques d’une migration mal faite

Trois erreurs reviennent systématiquement sur les audits de projets en cours de migration Pages Router vers App Router :

  • Copier-coller l’ancien composant avec 'use client' ajouté en haut « pour que ça compile », sans redécouper l’arbre — le bundle ne bouge pas d’un octet.
  • Utiliser des Context Providers React (thème, i18n, état global) en haut de layout, ce qui force tout le sous-arbre en client alors qu’une partie aurait pu rester serveur.
  • Oublier la revalidation des données : sans stratégie de cache explicite (revalidate, cache: 'no-store'), un Server Component peut servir des données périmées ou, à l’inverse, retaper l’API à chaque requête sans nécessité.

Ce dernier point demande une vraie décision projet par projet : contenu éditorial qui change une fois par jour, stock qui change en temps réel, panier propre à chaque utilisateur — chacun a sa stratégie de cache, et le mélange des trois dans une architecture non pensée fait exploser soit la fraîcheur des données, soit la charge serveur.

Quand migrer, et quand ne pas migrer

Un projet neuf en Next.js n’a aucune raison de partir sur Pages Router aujourd’hui : l’App Router avec Server Components est le chemin par défaut. Pour un projet existant en Pages Router qui tourne correctement, la migration se justifie surtout si le SEO ou le PageSpeed mobile sont des points de douleur mesurés, pas par principe — une réécriture complète a un coût réel en temps de développement et en risque de régression fonctionnelle.

C’est le même raisonnement que sur un projet WordPress construit à coups de page builder : le problème n’est pas l’outil en tant que tel, c’est l’accumulation de couches qui alourdissent chaque page sans qu’on le remarque avant de mesurer. Un site WordPress sur mesure en thème PHP natif applique le même principe côté CMS : n’envoyer au navigateur que ce dont la page a réellement besoin, sans dépendance superflue.

FAQ

Faut-il tout réécrire pour passer aux Server Components ?

Non. Si le projet est déjà en App Router, la bascule se fait composant par composant : on retire progressivement les 'use client' superflus et on redescend les composants interactifs dans l’arbre, sans réécriture globale. Si le projet est encore en Pages Router, la migration passe par une adoption progressive de l’App Router, dossier par dossier, ce qui permet de garder le site en production pendant la transition.

Les Server Components ralentissent-ils le serveur si beaucoup d’utilisateurs arrivent en même temps ?

Le rendu se fait bien côté serveur à chaque requête, sauf mise en cache explicite, donc la charge serveur augmente par rapport à du contenu 100 % statique. C’est pour ça que la stratégie de cache et de revalidation n’est pas un détail : bien réglée avec le système de cache de Next.js, la charge reste comparable à du SSR classique, avec un gain net côté client sur le poids de la page.

Est-ce compatible avec une librairie tierce comme un slider ou une carte interactive ?

Oui, à condition de l’encapsuler dans un Client Component dédié avec 'use client', importé depuis un Server Component parent. La librairie continue de tourner côté navigateur comme avant ; ce qui change, c’est que le reste de la page autour d’elle n’a plus besoin d’embarquer du JavaScript pour du contenu statique.

Pour un projet qui doit tenir un score PageSpeed mobile 90+ dès la mise en ligne, la meilleure fenêtre pour bien poser cette architecture reste le départ du projet : un devis détaillé sous 48h permet de cadrer la répartition Server / Client Components avant d’écrire la première ligne.

À lire ensuite · Next.js

Tous les articles Next.js →

Un front-end Next.js ?

SSR, headless CMS, applications métier — deux projets Next.js en production, vérifiables.

Développement Next.js