Un peu de contexte : pourquoi tout ce chantier ?
Depuis une bonne dizaine d'années, on a suivi une logique assez simple avec React : tout se passe côté client. Le navigateur télécharge un gros paquet de JavaScript, l'exécute, construit le DOM, fait ses appels API, et affiche enfin quelque chose. Confortable pour les développeurs, beaucoup moins pour les utilisateurs qui regardent un écran blanc pendant deux secondes sur une connexion 4G moyenne.
Les React Server Components (RSC) sont la réponse de l'équipe React à ce problème. L'idée n'est pas nouvelle en soi (le SSR existe depuis longtemps), mais l'approche est radicalement différente : au lieu de tout envoyer au client puis tout exécuter là-bas, on garde une partie du travail sur le serveur, en permanence, sans jamais l'envoyer au navigateur.
Server Components, Client Components : qui fait quoi ?
Avec Next.js (App Router) ou d'autres frameworks compatibles, chaque composant est par défaut un Server Component. Il s'exécute uniquement sur le serveur, génère du HTML, et ce HTML arrive tout prêt chez l'utilisateur. Pas de JavaScript associé envoyé au client pour ce composant précis.
Dès que vous avez besoin d'interactivité (un clic, un état local, un useEffect), vous passez en Client Component avec la directive magique tout en haut du fichier :
'use client';
import { useState } from 'react';
export default function Compteur() {
const [count, setCount] = useState(0);
return (
);
}Sans cette directive, impossible d'utiliser useState, useEffect ou tout hook qui dépend du navigateur. Le compilateur vous le rappellera d'ailleurs assez sèchement.
Un exemple concret : une page produit e-commerce
Imaginez une fiche produit. La description, le prix, les avis clients : tout ça peut être un Server Component, récupéré directement en base de données sans passer par une API REST intermédiaire.
// app/produits/[id]/page.js (Server Component par défaut)
import { getProduit } from '@/lib/db';
import BoutonAjoutPanier from './BoutonAjoutPanier';
export default async function PageProduit({ params }) {
const produit = await getProduit(params.id);
return (
{produit.nom}
{produit.description}
{produit.prix} €
);
}Seul le bouton \"Ajouter au panier\" (qui a besoin d'un onClick) devient un Client Component. Tout le reste ? Zéro JavaScript envoyé au navigateur pour l'afficher. On appelle directement la base de données dans le composant lui-même, sans exposer d'API publique inutile.
Les vrais avantages, sans le blabla marketing
- Un bundle JS plus léger : seuls les composants réellement interactifs envoient du code au client. Sur un site avec beaucoup de contenu statique (blog, fiche produit, page vitrine), le gain peut être énorme.
- Accès direct aux données : plus besoin de créer une route API juste pour qu'un composant client puisse récupérer des données. Le Server Component tape directement dans la base ou le CMS.
- Sécurité renforcée : vos clés d'API, vos requêtes SQL, votre logique métier sensible restent côté serveur. Rien ne fuite dans le code source visible par l'utilisateur.
- Meilleur temps de chargement perçu : le HTML arrive déjà construit, l'utilisateur voit du contenu avant même que React ait fini de s'hydrater côté client.
Les pièges dans lesquels tout le monde tombe
Bon, soyons honnêtes, ce n'est pas non plus la solution miracle sans contrepartie. Il y a quelques subtilités qui piquent au début.
La confusion server/client
Le réflexe naturel est de mettre 'use client' un peu partout "pour être tranquille". Résultat : on recrée exactement le problème qu'on essayait de résoudre, avec un bundle client aussi lourd qu'avant. Le bon réflexe consiste à pousser les Client Components le plus bas possible dans l'arbre de composants, uniquement là où l'interactivité est réellement nécessaire.
Impossible de passer des fonctions en props
Un Server Component ne peut pas passer une fonction (comme un callback) à un Client Component, sauf via les Server Actions. Ça demande de repenser certains patterns habituels.
// Server Action : s'exécute côté serveur mais appelable depuis le client
'use server';
export async function ajouterAuPanier(produitId) {
await db.panier.create({ produitId });
}Le débogage change de nature
Un console.log dans un Server Component ? Il n'apparaîtra pas dans la console du navigateur, mais dans le terminal de votre serveur Next.js. Petit détail qui fait perdre dix bonnes minutes la première fois qu'on tombe dessus, on ne va pas se mentir.
Comment se lancer concrètement ?
Si vous partez d'un projet Next.js récent (13+) avec l'App Router, vous avez déjà tout sous la main. La migration la plus simple consiste à :
- Identifier les composants purement affichage/données (listes, fiches, textes statiques) → les laisser en Server Components.
- Isoler dans des fichiers séparés les éléments qui ont vraiment besoin d'état ou d'événements → ajouter
'use client'uniquement là. - Remplacer les appels
fetchvers vos propres API internes par des appels directs à votre couche de données quand c'est un Server Component.
Pour les projets existants en Pages Router ou avec Vite/CRA, la transition demande davantage de réflexion : les RSC ne sont pas (encore) une simple case à cocher, ils impliquent de repenser l'architecture de vos composants.
Notre avis chez YouOnline
On accompagne pas mal de clients sur des refontes de sites vitrines et e-commerce, et honnêtement, les Server Components changent vraiment la donne sur les performances perçues, surtout sur mobile. Ce n'est pas magique, ça demande un peu d'apprentissage et de discipline dans l'architecture du code, mais le jeu en vaut clairement la chandelle pour un site avec beaucoup de contenu à afficher rapidement.
Si votre projet actuel commence à traîner des pieds côté performance, c'est peut-être le bon moment de se pencher sérieusement sur la question. On ne va pas dire que c'est la solution à tout, mais dans pas mal de cas, ça coche vraiment les bonnes cases.
