aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Développement Web

Next.js Server Components : lequel choisir et quand

Dès que vous ouvrez un projet avec le App Router de Next.js, vous évoluez par défaut dans le monde des Next.js Server Components. L'ancien modèle auquel nous étions habitués — « chaque composant React s'exécute dans le navigateur » — a laissé place à une architecture en deux parties où certains composants sont rendus sur le serveur et seuls quelques-uns sont envoyés au navigateur. Dans cet article, j'explique la vraie différence entre Server Components et Client Components, comment ils fonctionnent ensemble, et lequel choisir au quotidien, avec des exemples concrets.

Que sont les Server Components et les Client Components ?

La distinction tient à l'endroit un composant s'exécute.

  • Server Component (RSC) : s'exécute uniquement sur le serveur. Sa sortie est envoyée au navigateur sous forme de HTML accompagné d'une charge sérialisée spéciale ; le JavaScript du composant n'est pas envoyé au client. Il peut accéder directement à la base de données, au système de fichiers ou à des clés d'API secrètes.
  • Client Component : rendu une fois sur le serveur, puis « hydraté » (rendu interactif) dans le navigateur. useState, useEffect, les écouteurs d'événements et les API du navigateur vivent ici.

Dans l'App Router, chaque composant est un Server Component par défaut. Pour transformer un composant en Client Component, on ajoute la directive "use client" en haut du fichier.

Que fait réellement « use client » ?

Une idée fausse courante veut qu'un fichier marqué "use client" s'exécute « uniquement dans le navigateur ». En réalité, ce composant est tout de même rendu sur le serveur à la première requête ; le rôle de la directive est de marquer la frontière client (client boundary) qui commence dans ce fichier. À partir de cette frontière, le composant et ce qu'il importe sont inclus dans le bundle client.

"use client";

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Compteur : {count}
    </button>
  );
}

La directive doit figurer tout en haut du fichier, avant tout import. Une fois la frontière d'un Client Component franchie, tous les composants enfants rendus à l'intérieur sont eux aussi évalués côté client.

Lequel choisir, et quand ?

La règle pratique est simple : laissez la valeur par défaut en Server Component et ne passez en Client que lorsque vous avez besoin d'interactivité ou d'une API du navigateur.

Préférez un Server Component quand :

  • Vous récupérez des données depuis une base de données ou une API (directement avec async/await).
  • Vous utilisez des clés, des tokens ou des chaînes de connexion qui doivent rester secrets.
  • Vous voulez écarter une dépendance lourde (un parseur Markdown ou une bibliothèque de dates, par exemple) du bundle client.
  • Le contenu est largement statique, sans interaction.

Préférez un Client Component quand :

  • Vous avez besoin d'état (useState, useReducer) ou du cycle de vie (useEffect).
  • Vous utilisez des écouteurs d'événements comme onClick ou onChange.
  • Vous avez besoin d'API propres au navigateur comme window, localStorage ou IntersectionObserver.
  • Vous dépendez d'une bibliothèque uniquement côté client (de nombreux composants d'animation ou de cartographie, par exemple).

Utiliser les deux ensemble : le motif de composition

La vraie puissance réside dans l'imbrication des deux. Le motif courant et recommandé consiste à récupérer les données sur le serveur et à les passer à un petit Client Component en tant que prop. Vous gardez ainsi la partie interactive minimale et laissez le gros du travail sur le serveur.

// app/page.tsx  (Server Component)
import Counter from "./counter";

export default async function Page() {
  const data = await getData(); // s'exécute sur le serveur
  return (
    <main>
      <h1>{data.title}</h1>
      <Counter />        {/* frontière client */}
    </main>
  );
}

Un détail important : vous ne pouvez pas importer et rendre un Server Component à l'intérieur d'un Client Component, mais vous pouvez le passer comme children. Cela permet d'afficher du contenu Server Component dans un wrapper client (un fournisseur de thème, par exemple) :

// Client Component
"use client";
export default function Panel({ children }) {
  return <div className="panel">{children}</div>;
}

// Server Component
<Panel>
  <ServerContent />   {/* passé comme children, reste sur le serveur */}
</Panel>

Erreurs fréquentes

  • Mettre "use client" partout. Cela annule tous les bénéfices des RSC ; le bundle grossit et le premier chargement ralentit. Poussez la directive le plus près possible du composant feuille (le plus interne et interactif).
  • Vouloir utiliser useState ou un gestionnaire d'événement dans un Server Component. Cela produit une erreur de build/d'exécution ; il faut transformer le composant en Client Component.
  • Déplacer une clé secrète dans un Client Component. Tout ce qui atterrit dans le bundle client est visible par tous. Les secrets doivent rester uniquement dans les Server Components ou le code serveur.
  • Écrire un Client Component async. Les Server Components peuvent être async pour récupérer des données ; pour récupérer des données dans un Client Component, utilisez useEffect ou une bibliothèque comme SWR.

Questions fréquentes

Les Server Components ont-ils remplacé getServerSideProps ?

En grande partie, oui. L'App Router n'a plus de getServerSideProps ni de getStaticProps ; vous récupérez les données directement avec await dans un Server Component async. Le comportement de cache et de revalidation se contrôle ensuite via les options de fetch.

Un Client Component peut-il recevoir un Server Component comme enfant ?

Oui. Un Client Component ne peut pas importer et rendre un Server Component directement, mais il peut en recevoir un via children ou une autre prop. C'est la manière standard de combiner des wrappers interactifs avec du contenu rendu côté serveur.

Pourquoi useState ne fonctionne-t-il pas dans un Server Component ?

Parce que useState nécessite un état qui vit dans le navigateur et persiste entre les rendus ; un Server Component n'a pas de cycle de vie côté client. Lorsque vous avez besoin d'état ou d'interactivité, isolez cette partie dans un Client Component.

Vous voulez structurer correctement votre architecture Next.js ? Je peux vous aider à bâtir une application rapide et maintenable qui équilibre bien Server et Client Components. Contactez-moi.

Bu kategorideki tüm yazılar →

Devamı için