Next.js App Router : comment transmettre des données entre un composant serveur et un composant client ?
Dans le Next.js App Router, une page peut charger ses données côté serveur puis confier une partie de l’affichage à un composant client interactif. Le passage entre les deux ne repose pas sur un partage libre de mémoire : il s’effectue principalement par des props, au travers d’une frontière serveur-client que le framework construit lors du rendu.
Pour une équipe qui développe un tableau de bord, cette distinction change la manière d’organiser chaque écran. Les données sensibles ou lentes à obtenir peuvent rester dans un composant serveur, tandis que les contrôles nécessitant des clics, du clavier ou un état local sont confiés aux Client Components. La transmission de données devient ainsi une décision d’architecture autant qu’un détail de code.
En bref
- Un composant serveur peut transmettre des données à un composant client en les lui passant comme props.
- Les valeurs doivent respecter les règles de sérialisation de React ; une fonction ordinaire ne se transmet pas comme une prop classique.
- Le composant client doit être identifié par la directive « use client » et ne doit pas recevoir inutilement des données confidentielles.
- Pour actualiser les données, il faut choisir une stratégie explicite : nouvelle navigation, rafraîchissement, action serveur ou requête côté client.
Next.js App Router : comprendre la frontière entre composant serveur et composant client
Dans l’App Router, les composants d’une page sont des Server Components par défaut. Ils peuvent s’exécuter côté serveur, accéder aux ressources nécessaires au rendu et produire une interface sans envoyer au navigateur le code de leur logique serveur. Un composant client répond à un besoin différent : il prend en charge l’interactivité, par exemple un menu déroulant, un formulaire réactif ou un compteur modifiable.
La séparation est matérialisée par la directive « use client », placée au début du fichier concerné. Elle ne signifie pas que chaque élément affiché par ce composant a nécessairement été calculé dans le navigateur ; elle définit une frontière à partir de laquelle le module et ses dépendances sont destinés à participer au rendu client et à l’hydratation.
Imaginons le tableau de bord d’une petite librairie, tenue par une équipe fictive appelée Atelier Papier. La page serveur récupère le nom de la boutique et le nombre de commandes en attente, puis transmet ces valeurs à un composant client qui affiche un bouton permettant de filtrer les commandes. La page garde la responsabilité du chargement initial, alors que le filtre gère l’interaction immédiate.
Cette organisation évite de transformer toute la page en composant client par commodité. Si un composant racine porte « use client », ses dépendances entrent aussi dans le graphe client ; le JavaScript livré peut alors augmenter et des capacités serveur ne sont plus disponibles dans cette partie. En plaçant la frontière au plus près de l’élément interactif, l’interface conserve davantage de travail côté serveur.
Il faut également distinguer la direction du rendu de celle des données. Un composant client ne peut pas importer et exécuter directement un composant serveur comme s’il s’agissait d’une fonction ordinaire. En revanche, un composant serveur peut rendre un composant client et lui fournir des propriétés compatibles, ou lui transmettre du contenu déjà rendu sous forme de children.
Dans le cas d’Atelier Papier, le composant interactif reçoit seulement les informations dont le filtre a besoin, et non l’ensemble des résultats confidentiels. La frontière serveur-client devient alors un contrat lisible : le serveur prépare, le navigateur interagit, et chaque valeur franchissant cette limite a une fonction précise.
La transmission de données commence donc par une question simple : quelle partie doit calculer la valeur, et quelle partie doit la manipuler ?
Transmettre des props après un fetch de données côté serveur
Un scénario courant consiste à effectuer le fetch de données dans une page ou un composant serveur asynchrone, puis à fournir le résultat à un composant client. Le serveur peut appeler une API ou consulter une source adaptée à son environnement avant le rendu. Il transmet ensuite uniquement les champs utiles à l’expérience interactive, plutôt que de faire recommencer la même requête au navigateur.
Pour Atelier Papier, la page peut récupérer une liste de commandes depuis un service interne, sélectionner un identifiant, un statut et un montant affichable, puis passer ces valeurs au panneau client. Le panneau utilise les props reçues pour construire la vue et mémoriser, dans son état local, le filtre choisi. Le chargement initial et le comportement d’interface restent ainsi deux responsabilités distinctes.
Une prop constitue une valeur d’entrée, pas un canal de synchronisation permanent. Si la donnée change plus tard sur le serveur, le composant client ne reçoit pas spontanément une mise à jour par magie ; il faut que l’application déclenche un nouveau rendu ou adopte une stratégie d’actualisation. Cette distinction évite les bugs où l’état local continue d’afficher un ancien statut après une mutation.
Le choix des champs transmis compte aussi pour les performances. Passer une collection entière de milliers d’objets à un petit composant client peut accroître la charge transférée et rendre l’interface plus difficile à maintenir. Un objet adapté à l’usage, comme un résumé de commande plutôt que l’enregistrement complet, rend le contrat plus explicite et réduit les données exposées.
Lorsque le serveur doit garder un secret, par exemple une clé d’API ou un jeton interne, cette valeur ne doit jamais figurer dans les props destinées au navigateur. Le composant client ne peut pas protéger une information qui lui a été envoyée : elle peut être inspectée dans les outils du navigateur ou dans les données de rendu. Il convient de transformer les résultats côté serveur et de ne transmettre que ce que l’utilisateur est autorisé à voir.
Cette organisation est utile pour les interfaces de formulaires. Le serveur peut fournir les options disponibles et les valeurs initiales, tandis que le client valide immédiatement une saisie ou affiche un état de chargement. Pour découvrir une autre approche de l’interactivité autour des formulaires, un exemple de formulaire interactif construit avec Vue.js permet de comparer les responsabilités de l’interface.
Dans l’exemple, le filtrage est fluide parce que les données nécessaires sont déjà présentes, mais les règles métier et les écritures demeurent contrôlées côté serveur. Une prop bien choisie fournit au navigateur son contexte d’action sans lui transférer la responsabilité de l’accès aux données.

Sérialisation des données entre Server Components et Client Components
Le passage de props ne consiste pas à envoyer des objets JavaScript arbitraires à travers le réseau. Next.js s’appuie sur le mécanisme de rendu de React pour représenter les informations nécessaires à l’interface ; les valeurs doivent donc respecter les règles de sérialisation applicables à cette frontière. Les chaînes de caractères, nombres, booléens, tableaux et objets simples couvrent la majorité des besoins courants.
Une fonction ordinaire, une connexion de base de données ou une instance contenant des méthodes ne constitue pas une prop classique à transmettre. Ces éléments représentent du comportement ou des ressources propres au serveur, pas des données destinées à être reconstruites dans le navigateur. Pour une date, il est souvent plus lisible de transmettre une chaîne ISO et de gérer explicitement son formatage côté client, notamment si l’affichage dépend du fuseau horaire.
Les capacités exactes de sérialisation sont liées à React et à la version utilisée ; mieux vaut vérifier la documentation correspondante lorsqu’un type particulier est en jeu. L’erreur de transmission indique souvent qu’une valeur n’est pas prise en charge ou que le contrat entre les deux composants contient une ressource qui devrait rester côté serveur. Une transformation en données simples rend généralement le flux plus prévisible.
Supposons que la réponse de la librairie contienne une date d’expédition, un objet de suivi complexe et une méthode interne de calcul. Le composant serveur peut préparer un objet de présentation avec une date normalisée, une étiquette de statut et un lien autorisé. Le Client Component reçoit alors ce dont il a besoin pour ouvrir un panneau de suivi, sans embarquer la méthode métier ni l’objet brut.
Il existe une exception importante à l’idée qu’aucune fonction ne peut franchir cette frontière : les Server Actions sont conçues pour permettre certaines opérations déclenchées depuis une interface cliente. Elles ne transmettent pas une fonction JavaScript ordinaire comme une prop habituelle ; elles exposent une action serveur selon le mécanisme prévu par React et Next.js. Cette possibilité doit être appliquée avec validation et contrôle d’autorisation côté serveur.
Le contenu rendu peut également traverser la composition sous forme de children. Un composant serveur peut préparer une zone de contenu et la fournir à un composant client qui gère, par exemple, l’ouverture d’un panneau. Cela permet de conserver le rendu des éléments non interactifs sur le serveur, tout en donnant au conteneur client le contrôle d’un comportement d’interface.
Pour diagnostiquer un problème, il est utile d’inspecter la forme de chaque prop au moment du passage : s’agit-il d’une donnée simple, d’un objet transformable ou d’une ressource qui n’a rien à faire dans le navigateur ? La sérialisation n’est pas une formalité technique ; elle aide à révéler où se situe réellement la responsabilité de chaque valeur.
Un contrat de props sobre réduit à la fois les erreurs de rendu et les fuites d’information, ce qui rend la frontière plus facile à auditer.
Actualiser les données transmises sans confondre props et état client
Une prop correspond à l’état initial remis au composant client lors du rendu, tandis que l’état local sert aux changements immédiats produits par l’interaction. Dans le tableau de bord, le nombre de commandes reçu du serveur peut initialiser l’écran, alors que le choix « en attente » ou « expédiée » relève du contrôle client. Confondre ces deux niveaux conduit souvent à des interfaces qui semblent réagir mais ne reflètent plus les données réelles.
Après une modification, plusieurs parcours sont possibles. Une action serveur peut valider la demande, effectuer l’écriture et renvoyer un résultat ; la page peut ensuite être actualisée pour reconstruire ses données serveur. Selon le cas, un rafraîchissement de route ou une nouvelle navigation permet au composant serveur de relancer son fetch de données, avec le comportement de cache configuré pour cette ressource.
Une équipe peut aussi choisir une requête déclenchée depuis le client lorsqu’une donnée doit se mettre à jour indépendamment de la navigation, par exemple un indicateur de disponibilité fréquemment renouvelé. Cette option ajoute une responsabilité au navigateur : gérer le chargement, les erreurs et éventuellement l’annulation d’une requête devenue obsolète. Elle ne remplace pas les contrôles d’accès côté serveur.
Un exemple concret : une employée marque une commande comme expédiée. L’interface peut afficher immédiatement un état d’attente pour éviter un bouton bloqué, puis appeler une action autorisée qui vérifie l’utilisateur et met à jour la base. Une fois la mutation confirmée, l’application rafraîchit la vue ou met à jour les éléments concernés selon une stratégie définie.
Le cache mérite une attention distincte. Une réponse déjà mise en cache peut être parfaitement appropriée pour un catalogue peu changeant, mais problématique pour un stock dont la disponibilité doit être fiable. Il faut configurer le comportement du fetch en fonction de la fraîcheur attendue, et non supposer que chaque rendu provoque systématiquement un appel réseau neuf.
La transmission initiale des props et la mise à jour ultérieure sont donc deux mécanismes différents. Les props établissent le contexte au rendu ; une action, une navigation, un rafraîchissement ou une requête client déclenche le changement. Cette séparation aide à choisir une solution sans transformer le composant client en source de vérité pour les données métier.
Pour les petits écrans, un rafraîchissement après mutation suffit souvent et garde une logique simple. Pour une interface collaborative en temps réel, une stratégie dédiée peut être nécessaire, mais elle doit être justifiée par le besoin plutôt que par une volonté abstraite de rendre tout « dynamique ».
Le bon choix est celui qui garde cohérents l’état visible, la réponse du serveur et les règles métier, même lorsqu’une requête échoue ou qu’un utilisateur ouvre plusieurs onglets.
Organiser une frontière serveur-client fiable dans un projet Next.js
La conception commence par un inventaire des besoins : quelles données sont nécessaires au premier affichage, quelles actions exigent une interaction et quelles informations doivent rester confidentielles ? Cette méthode évite d’ajouter « use client » à une page entière alors qu’un seul bouton ou formulaire a besoin d’état local. Elle facilite aussi la revue de code, car chaque frontière a une raison compréhensible.
Dans un écran de commandes, le composant serveur peut charger les résultats et rendre les lignes statiques, tandis qu’un petit Client Component pilote un menu de tri. Si le tri exige la totalité des données, les props peuvent contenir une collection réduite ; si les résultats sont volumineux, transmettre un critère et demander au serveur une nouvelle sélection peut être préférable. Le volume et la sensibilité des données déterminent l’architecture.
Un tableau aide à choisir le mécanisme selon le besoin, sans traiter toutes les formes de communication comme équivalentes.
| Besoin | Approche adaptée | Point de vigilance |
| Afficher des données au premier rendu | Fetch côté serveur puis props | Ne transmettre que les champs utiles |
| Réagir à un clic ou à une saisie | État local dans un Client Component | L’état local ne remplace pas les règles métier |
| Modifier une donnée persistante | Action serveur ou endpoint contrôlé | Valider l’entrée et l’autorisation |
| Actualiser des résultats | Rafraîchissement, navigation ou requête client | Définir la fraîcheur et le cache attendus |
Un contrôle pratique consiste à se demander si la donnée serait sans risque dans le code source livré au navigateur. Si la réponse est non, elle ne doit pas devenir une prop client. Un autre contrôle porte sur la taille : une liste entière n’a pas à traverser la frontière lorsque le composant n’affiche qu’un compteur et un libellé.
Les erreurs de sérialisation sont aussi des indices de conception. Plutôt que de contourner le problème en déplaçant tout le code côté client, il est préférable d’identifier l’instance ou la fonction qui franchit la limite, puis de la remplacer par une représentation adaptée. Pour les informations d’état d’un équipement, par exemple, transmettre un libellé et une valeur normalisée est souvent plus pertinent que de fournir l’objet complet d’une bibliothèque spécialisée.
Une lecture complémentaire sur une notification de réseau domestique, comme l’explication de la mention « signal FTTH détecté », illustre utilement la différence entre une donnée compréhensible par l’utilisateur et les détails techniques internes qui l’ont produite. Dans une interface, le serveur peut pareillement transformer un résultat technique en information claire avant de le transmettre.
Enfin, tester le parcours complet est plus efficace que vérifier seulement l’affichage initial. Il faut examiner le rendu sans JavaScript, l’hydratation, les valeurs transmises, l’échec d’une mutation et le comportement après actualisation. Une frontière bien définie rend ces tests plus ciblés : le serveur fournit des données fiables et le navigateur ajoute l’interaction requise.
La qualité de l’architecture se mesure ainsi à la clarté des responsabilités : moins de données inutiles traversent la frontière, plus le comportement devient prévisible.
Un tutoriel visuel peut compléter cette méthode en montrant le trajet d’une donnée depuis le fetch initial jusqu’à son affichage interactif. Le point à retenir reste le contrat explicite entre les deux types de composants.
Comment transmettre une donnée d’un composant serveur à un composant client ?
Le composant serveur rend le composant client et lui passe la donnée comme prop. La valeur doit être compatible avec les règles de sérialisation de React et ne contenir aucune information secrète destinée à rester côté serveur.
Faut-il ajouter « use client » à la page entière ?
Non. La directive doit être placée au début du module qui a besoin d’interactivité. Garder la frontière près des contrôles interactifs limite le code envoyé au navigateur et conserve le rendu des parties statiques côté serveur.
Une prop client se met-elle à jour automatiquement après une modification ?
Non. Une prop ne constitue pas un canal de synchronisation permanent. Après une mutation, l’application doit déclencher une nouvelle navigation ou un rafraîchissement, utiliser une action serveur, ou actualiser les données depuis le client selon le besoin.
Peut-on passer une fonction ordinaire en prop ?
Une fonction JavaScript ordinaire ne se transmet pas comme une prop classique à travers la frontière serveur-client. Pour déclencher une opération serveur, les Server Actions fournissent un mécanisme spécifique, qui doit toujours valider les entrées et les autorisations.







