
Je suis Mathieu Ponton, Co-Founder & ingénieur logiciel chez Apogée Consult à Lyon. Ingénieur diplômé de Polytech Lyon (Informatique), j'ai fait trois ans en apprentissage, partagés entre la Métropole de Lyon (inclusion numérique avec Res'in et sobriété énergétique avec Écolyo) et Superwyze, une startup medtech (POCs, dont certains aujourd'hui industrialisés, et travail sur des codebases existantes). J'ai livré plus de 10 projets en production (web, mobile et IA / RAG) pour des PME, startups et organisations publiques.
React Server Components : gain de performance mesuré sur 3 de nos projets
- perf
Nous avons mesuré l'impact des RSC sur trois projets en production. Les chiffres sont réels, les limites aussi. Voici ce qu'on a appris.
React Server Components : gain de performance mesuré sur 3 de nos projets
Les React Server Components font partie de l'architecture Next.js App Router depuis Next 13.4. En 2025, plusieurs de nos projets ont suffisamment de recul pour produire des mesures fiables. Ce retour d'expérience couvre trois projets de taille différente, avec des profils de charge distincts. Les chiffres sont issus de Lighthouse CI, de WebPageTest, et de nos propres scripts de profilage.
Les trois projets mesurés
Projet A, site vitrine B2B avec blog : environ 80 composants, aucune interactivité complexe, contenu géré via CMS headless. Migration depuis Next.js Pages Router vers App Router en conservant les mêmes données.
Projet B, application SaaS interne : tableau de bord avec grilles de données, filtres, formulaires. Mix important de composants serveur et client. La partie table est restée Client Component pour des raisons d'interactivité.
Projet C, catalogue produits e-commerce : 4 000 SKUs, pages produit générées en ISR, panier en Client Component, reste en Server Components.
Ce que les RSC changent sur le bundle JS
Le gain le plus mesurable est la réduction du bundle JavaScript envoyé au client.
Sur le Projet A, la migration a supprimé du bundle client tout le code de fetching de données, les librairies de parsing markdown, et les composants d'affichage pur. Résultat mesuré sur la page d'accueil :
| Métrique | Pages Router | App Router RSC | Variation |
|---|---|---|---|
| Bundle JS (gzippé) | 187 ko | 94 ko | -50% |
| Temps d'hydration | 1,2 s | 0,4 s | -67% |
| LCP (p75 Lighthouse) | 2,8 s | 1,9 s | -32% |
Ces chiffres sont cohérents avec ce que Vercel publie dans ses benchmarks publics. La réduction de bundle est proportionnelle à la part du code qui peut rester côté serveur, un composant qui ne fait qu'afficher des données est le candidat idéal.
Sur le Projet B, le gain est beaucoup plus faible. Le dashboard nécessite des Client Components pour les interactions, et les librairies d'UI (tables, graphiques) restent dans le bundle client. La réduction observée : environ 15% sur le bundle JS global, sans impact significatif sur le LCP ou le FID.
Sur le Projet C, l'impact est intermédiaire. Les pages produit (Server Components) sont nettement plus légères. Mais le panier et les composants de filtre dynamique maintiennent un bundle client substantiel.
L'impact sur le TTFB
C'est la contrepartie qu'on voit rarement documentée : les RSC augmentent le Time To First Byte sur les routes dynamiques.
Avec les Server Components, le serveur doit terminer le rendu, y compris les appels réseau aux APIs ou à la base de données, avant d'envoyer la réponse. Si votre page effectue trois appels en cascade côté serveur, le TTFB reflète ce temps d'attente.
Mesures sur le Projet C, page produit dynamique :
- TTFB moyen Pages Router (avec getServerSideProps) : 180 ms
- TTFB moyen App Router avec RSC : 240 ms
Cet écart s'explique par la manière dont Next.js sérialise le rendu RSC. La différence est compensée sur le ressenti utilisateur par la réduction du temps de chargement JS et d'hydration, mais elle est réelle.
La mitigation standard est le streaming via Suspense :
import { Suspense } from "react";
import { ProductDetails } from "./ProductDetails";
import { RelatedProducts } from "./RelatedProducts";
export default function ProductPage({ params }: { params: { id: string } }) {
return (
<main>
<Suspense fallback={<ProductSkeleton />}>
<ProductDetails id={params.id} />
</Suspense>
<Suspense fallback={<RelatedSkeleton />}>
<RelatedProducts id={params.id} />
</Suspense>
</main>
);
}Avec ce pattern, le navigateur reçoit le shell HTML immédiatement, et les blocs de contenu arrivent en streaming au fur et à mesure que les requêtes serveur se terminent. Le TTFB perçu correspond au premier byte du shell, et non à la fin du rendu complet.
Les erreurs qu'on a faites
Première erreur : abuser des "use client" pour simplifier la migration. Quand un composant existant levait une erreur liée aux hooks ou aux événements, l'instinct était d'ajouter "use client" en haut du fichier. Sur le Projet B, nous avons retrouvé des composants de pure affichage marqués Client Component inutilement, ce qui annulait le gain de bundle.
La règle qu'on a adoptée : "use client" uniquement si le composant utilise useState, useEffect, des event handlers, ou des APIs browser. Jamais par défaut.
Deuxième erreur : des appels de données redondants. Avec Pages Router, getServerSideProps centralisait le fetching pour toute la page. Avec RSC, il est tentant de fetcher les données dans chaque composant indépendamment. Si deux composants de la même page appellent le même endpoint, vous doublez les requêtes.
Next.js met en cache automatiquement les appels fetch identiques dans le même cycle de rendu (request deduplication), mais uniquement pour les appels fetch natifs. Les appels via un ORM ou un SDK tiers ne sont pas dédupliqués automatiquement. Nous avons dû centraliser certains appels dans des fonctions utilitaires partagées.
Troisième erreur : ne pas mesurer avant de migrer. Sur le Projet A, nous pensions que le LCP était contraint par le bundle JS. Il était en réalité contraint par une image non optimisée au-dessus de la ligne de flottaison. La migration RSC a amélioré le LCP, mais l'image était le vrai coupable. Sans baseline Lighthouse avant migration, on aurait mal attribué le gain.
Ce que les RSC ne règlent pas
Les Server Components n'améliorent pas les performances des parties interactives de l'application. Un dashboard complexe avec des tables paginées, des filtres temps réel, et des formulaires imbriqués reste un problème de Client Component. RSC réduit le coût des parties statiques ou semi-statiques, pas celui des interactions utilisateur.
Ils ne règlent pas non plus les problèmes de bases de données lentes, d'APIs tierces à latence variable, ou de CDN mal configuré. Le rendu serveur déplace le problème de performance côté serveur, il ne le fait pas disparaître.
La question qui reste ouverte dans nos projets : quel est l'impact réel sur les Core Web Vitals en conditions de réseau dégradé (3G, connexion instable) ? Nos mesures sont faites sur des connexions câblées en environnement contrôlé. Les gains de bundle sont proportionnellement plus importants sur réseau lent, mais le TTFB augmenté peut inverser l'équation.