Rendu sur le Web

Publié le 6 février 2019, dernière mise à jour le 5 janvier 2026

L'une des principales décisions que les développeurs Web doivent prendre est de savoir où implémenter la logique et l'affichage dans leur application. Cela peut être difficile, car il existe de nombreuses façons de créer un site Web.

Notre compréhension de cet espace est basée sur notre travail dans Chrome, qui nous a permis de discuter avec de grands sites au cours des dernières années. De manière générale, nous encourageons les développeurs à envisager l'affichage côté serveur ou l'affichage statique plutôt qu'une approche de réhydratation complète.

Pour mieux comprendre les architectures parmi lesquelles nous choisissons lorsque nous prenons cette décision, nous avons besoin d'une terminologie cohérente et d'un framework partagé pour chaque approche. Vous pourrez ensuite mieux évaluer les compromis de chaque approche de rendu du point de vue des performances de la page.

Terminologie

Tout d'abord, nous allons définir certains termes que nous utiliserons.

Affichage

Rendu côté serveur (SSR)
Affichage d'une application sur le serveur pour envoyer du code HTML, plutôt que du code JavaScript, au client.
Rendu côté client (CSR)
Rendu d'une application dans un navigateur, à l'aide de JavaScript pour modifier le DOM.
Prérendu
Exécuter une application côté client au moment de la compilation pour capturer son état initial au format HTML statique. Notez que le "prérendu" dans ce sens est différent du prérendu du navigateur pour les navigations futures.
Hydratation
Exécution de scripts côté client pour ajouter l'état de l'application et l'interactivité au code HTML affiché par le serveur. L'hydratation suppose que le DOM ne change pas.
Réhydratation
Bien qu'elle soit souvent utilisée pour désigner la même chose que l'hydratation, la réhydratation implique de mettre à jour régulièrement le DOM avec l'état le plus récent, y compris après l'hydratation initiale.

Performances

Time to First Byte (TTFB)
Temps écoulé entre le clic sur un lien et le chargement du premier octet de contenu sur la nouvelle page.
First Contentful Paint (FCP)
Moment où le contenu demandé (corps de l'article, etc.) devient visible.
Interaction to Next Paint (INP)
Métrique représentative qui évalue si une page répond systématiquement rapidement aux entrées utilisateur.
Total Blocking Time (TBT)
Métrique proxy pour l'INP qui calcule la durée pendant laquelle le thread principal a été bloqué lors du chargement de page.

Affichage côté serveur

Le rendu côté serveur génère le code HTML complet d'une page sur le serveur en réponse à la navigation. Cela évite des allers-retours supplémentaires pour la récupération des données et la création de modèles sur le client, car le moteur de rendu les gère avant que le navigateur ne reçoive une réponse.

Le rendu côté serveur produit généralement un FCP rapide. En exécutant la logique de la page et l'affichage sur le serveur, vous évitez d'envoyer beaucoup de code JavaScript au client. Cela permet de réduire le TBT d'une page, ce qui peut également entraîner une diminution de l'INP, car le thread principal n'est pas bloqué aussi souvent lors du chargement de page. Lorsque le thread principal est moins souvent bloqué, les interactions utilisateur ont plus de chances de s'exécuter plus tôt.

C'est logique, car avec le rendu côté serveur, vous n'envoyez que du texte et des liens au navigateur de l'utilisateur. Cette approche peut fonctionner correctement pour différents types d'appareils et de conditions réseau, et permet d'optimiser le navigateur, par exemple en analysant les documents en streaming.

Diagramme montrant l'impact du rendu côté serveur et de l'exécution de JavaScript sur le FCP et le TTI.
FCP et TTI avec le rendu côté serveur.

Avec l'affichage côté serveur, les utilisateurs sont moins susceptibles d'attendre l'exécution du code JavaScript lié au processeur avant de pouvoir utiliser votre site. Même lorsque vous ne pouvez pas éviter le JavaScript tiers, l'utilisation du rendu côté serveur pour réduire vos propres coûts JavaScript first party peut vous donner plus de budget pour le reste. Toutefois, cette approche présente un inconvénient potentiel : la génération de pages sur le serveur prend du temps, ce qui peut augmenter le TTFB de votre page.

La question de savoir si le rendu côté serveur est suffisant pour votre application dépend en grande partie du type d'expérience que vous créez. Le débat sur les applications correctes du rendu côté serveur par rapport au rendu côté client est de longue date, mais vous pouvez toujours choisir d'utiliser le rendu côté serveur pour certaines pages et pas pour d'autres. Certains sites ont adopté des techniques de rendu hybride avec succès. Par exemple, Netflix effectue le rendu côté serveur de ses pages de destination relativement statiques, tout en prefetching le code JavaScript pour les pages à forte interaction, ce qui permet à ces pages plus lourdes d'être affichées plus rapidement.

Avec de nombreux frameworks, bibliothèques et architectures modernes, vous pouvez afficher la même application sur le client et sur le serveur. Vous pouvez utiliser ces techniques pour le rendu côté serveur. Cependant, les architectures où le rendu se produit à la fois sur le serveur et sur le client constituent une classe de solution à part entière, avec des caractéristiques de performances et des compromis très différents. Les utilisateurs de React peuvent utiliser les API DOM du serveur ou des solutions basées sur celles-ci, comme Next.js, pour le rendu côté serveur. Les utilisateurs de Vue peuvent utiliser le guide de rendu côté serveur de Vue ou Nuxt. Angular dispose d'Universal.

Toutefois, la plupart des solutions populaires utilisent une forme d'hydratation. Vous devez donc connaître les approches utilisées par votre outil.

Rendu statique

Le rendu statique se produit au moment de la compilation. Cette approche offre un FCP rapide, ainsi qu'un TBT et un INP inférieurs, à condition de limiter la quantité de code JavaScript côté client sur vos pages. Contrairement au rendu côté serveur, il permet également d'obtenir un TTFB toujours rapide, car le code HTML d'une page n'a pas besoin d'être généré de manière dynamique sur le serveur. En règle générale, le rendu statique consiste à produire à l'avance un fichier HTML distinct pour chaque URL. Avec les réponses HTML générées à l'avance, vous pouvez déployer des rendus statiques sur plusieurs CDN pour profiter de la mise en cache périphérique.

Diagramme montrant l'affichage statique et l'exécution JavaScript facultative affectant le FCP et le TTI.
FCP et TTI avec le rendu statique.

Les solutions de rendu statique sont de toutes formes et de toutes tailles. Des outils comme Gatsby sont conçus pour donner aux développeurs l'impression que leur application est affichée de manière dynamique, et non générée en tant qu'étape de compilation. Les outils de génération de sites statiques tels que 11ty, Jekyll et Metalsmith adoptent leur nature statique, offrant une approche davantage basée sur des modèles.

L'un des inconvénients du rendu statique est qu'il doit générer des fichiers HTML individuels pour chaque URL possible. Cela peut s'avérer difficile, voire impossible, lorsque vous devez prédire ces URL à l'avance et pour les sites comportant un grand nombre de pages uniques.

Les utilisateurs de React connaissent peut-être Gatsby, l'exportation statique Next.js ou Navi, qui permettent de créer facilement des pages à partir de composants. Cependant, le rendu statique et le prérendu se comportent différemment : les pages affichées de manière statique sont interactives sans qu'il soit nécessaire d'exécuter beaucoup de code JavaScript côté client, tandis que le prérendu améliore le FCP d'une application monopage qui doit être démarrée sur le client pour rendre les pages véritablement interactives.

Si vous ne savez pas si une solution donnée utilise le rendu statique ou le prérendu, essayez de désactiver JavaScript et de charger la page que vous souhaitez tester. Pour les pages affichées de manière statique, la plupart des fonctionnalités interactives existent toujours sans JavaScript. Les pages prérendues peuvent toujours comporter des fonctionnalités de base, comme des liens avec JavaScript désactivé, mais la majeure partie de la page est inerte.

Un autre test utile consiste à utiliser la limitation de bande passante réseau dans les outils pour les développeurs Chrome et à voir la quantité de code JavaScript téléchargée avant qu'une page ne devienne interactive. Le prérendu nécessite généralement plus de code JavaScript pour devenir interactif, et ce code JavaScript a tendance à être plus complexe que l'approche d'amélioration progressive utilisée dans le rendu statique.

Rendu côté serveur et rendu statique

Le rendu côté serveur n'est pas la meilleure solution pour tout, car sa nature dynamique peut entraîner des coûts de calcul importants. De nombreuses solutions de rendu côté serveur n'effectuent pas de vidage anticipé, retardent le TTFB ou doublent les données envoyées (par exemple, les états intégrés utilisés par JavaScript sur le client). Dans React, renderToString() peut être lent, car il est synchrone et monothread. Les API DOM de serveur React plus récentes sont compatibles avec le streaming, ce qui permet d'envoyer plus rapidement la partie initiale d'une réponse HTML au navigateur pendant que le reste est encore généré sur le serveur.

Pour que le rendu côté serveur soit "correct", il peut être nécessaire de trouver ou de créer une solution pour la mise en cache des composants, de gérer la consommation de mémoire, d'utiliser des techniques de mémorisation et d'autres problèmes. Vous traitez ou recréez souvent la même application deux fois, une fois sur le client et une fois sur le serveur. Le rendu côté serveur qui affiche le contenu plus tôt ne vous donne pas nécessairement moins de travail. Si vous avez beaucoup de travail sur le client après l'arrivée d'une réponse HTML générée par le serveur sur le client, cela peut toujours entraîner un TBT et un INP plus élevés pour votre site Web.

Le rendu côté serveur produit du code HTML à la demande pour chaque URL, mais il peut être plus lent que la simple diffusion de contenu statique affiché. Si vous pouvez effectuer le travail supplémentaire, l'affichage côté serveur et la mise en cache HTML peuvent réduire considérablement le délai d'affichage du serveur. L'avantage du rendu côté serveur est la possibilité d'extraire davantage de données "en direct" et de répondre à un ensemble de requêtes plus complet qu'avec le rendu statique. Les pages qui nécessitent une personnalisation sont un exemple concret du type de requête qui ne fonctionne pas bien avec l'affichage statique.

Le rendu côté serveur peut également présenter des décisions intéressantes lors de la création d'une PWA. Est-il préférable d'utiliser la mise en cache de service worker sur une page entière ou d'effectuer un rendu côté serveur de chaque élément de contenu ?

Rendu côté client

Le rendu côté client consiste à afficher les pages directement dans le navigateur avec JavaScript. Toute la logique, la récupération des données, la création de modèles et le routage sont gérés sur le client plutôt que sur le serveur. Le résultat effectif est que davantage de données sont transmises à l'appareil de l'utilisateur depuis le serveur, ce qui s'accompagne de ses propres compromis.

Le rendu côté client peut être difficile à créer et à maintenir rapide pour les appareils mobiles. En vous efforçant de respecter un budget JavaScript serré et de fournir de la valeur en un minimum d'allers-retours, vous pouvez obtenir un rendu côté client qui reproduit presque les performances d'un rendu purement côté serveur. Vous pouvez accélérer le fonctionnement de l'analyseur en fournissant des scripts et des données critiques à l'aide de <link rel=preload>. Nous vous recommandons également d'envisager d'utiliser des modèles tels que PRPL pour vous assurer que les navigations initiales et ultérieures semblent instantanées.

Schéma montrant l'impact du rendu côté client sur le FCP et le TTI.
FCP et TTI avec le rendu côté client.

Le principal inconvénient du rendu côté client est que la quantité de code JavaScript requise a tendance à augmenter à mesure que l'application se développe, ce qui peut avoir un impact sur l'INP d'une page. Cela devient particulièrement difficile avec l'ajout de nouvelles bibliothèques JavaScript, de polyfills et de code tiers, qui se disputent la puissance de traitement et doivent souvent être traités avant que le contenu d'une page puisse s'afficher.

Les expériences qui utilisent l'affichage côté client et s'appuient sur des bundles JavaScript volumineux devraient envisager une division agressive du code pour réduire le TBT et l'INP lors du chargement de page, ainsi que le chargement différé du code JavaScript pour ne diffuser que ce dont l'utilisateur a besoin, quand il en a besoin. Pour les expériences peu ou pas interactives, l'affichage côté serveur peut représenter une solution plus évolutive à ces problèmes.

Pour les personnes qui créent des applications monopages, l'identification des parties principales de l'interface utilisateur partagées par la plupart des pages vous permet d'appliquer la technique de mise en cache du shell d'application. Combiné aux service workers, cela peut améliorer considérablement les performances perçues lors des visites répétées, car la page peut charger son shell d'application HTML et ses dépendances à partir de CacheStorage très rapidement.

La réhydratation combine le rendu côté serveur et côté client

L'hydratation est une approche qui atténue les compromis entre le rendu côté client et le rendu côté serveur en effectuant les deux. Les requêtes de navigation, comme les chargements ou rechargements de pages complètes, sont gérées par un serveur qui affiche l'application au format HTML. Ensuite, le code JavaScript et les données utilisés pour l'affichage sont intégrés au document obtenu. Si elle est effectuée avec soin, cette méthode permet d'obtenir un FCP rapide, comme le rendu côté serveur, puis de "reprendre" en effectuant un nouveau rendu sur le client.

Cette solution est efficace, mais elle peut avoir des inconvénients considérables en termes de performances.

Le principal inconvénient du rendu côté serveur avec réhydratation est qu'il peut avoir un impact négatif important sur le TBT et l'INP, même s'il améliore le FCP. Les pages affichées côté serveur peuvent sembler chargées et interactives, mais elles ne peuvent pas répondre aux entrées tant que les scripts côté client des composants n'ont pas été exécutés et que les gestionnaires d'événements n'ont pas été associés. Sur mobile, cela peut prendre plusieurs minutes, ce qui peut dérouter et frustrer l'utilisateur.

Un problème de réhydratation : une application pour le prix de deux

Pour que le code JavaScript côté client prenne le relais là où le serveur s'est arrêté, sans demander à nouveau toutes les données avec lesquelles le serveur a affiché son code HTML, la plupart des solutions de rendu côté serveur sérialisent la réponse des dépendances de données d'une UI en tant que tags de script dans le document. Comme cela duplique beaucoup de code HTML, la réhydratation peut causer d'autres problèmes que le simple retard de l'interactivité.

Document HTML contenant l'UI sérialisée, les données intégrées et un script bundle.js.

Le serveur renvoie une description de l'UI de l'application en réponse à une requête de navigation, mais il renvoie également les données sources utilisées pour composer cette UI, ainsi qu'une copie complète de l'implémentation de l'UI qui démarre ensuite sur le client. L'UI ne devient interactive qu'une fois que bundle.js a fini de se charger et de s'exécuter.

Les métriques de performances collectées sur des sites Web réels utilisant l'affichage côté serveur et la réhydratation indiquent que ce n'est que rarement la meilleure option. La raison la plus importante est son effet sur l'expérience utilisateur, lorsqu'une page semble prête, mais qu'aucune de ses fonctionnalités interactives ne fonctionne.

Les effets négatifs du rendu côté client sur le TTI.

Il existe une solution : l'affichage côté serveur avec réhydratation. À court terme, l'utilisation du rendu côté serveur uniquement pour le contenu pouvant être mis en cache peut réduire le TTFB, ce qui produit des résultats similaires au prérendu. La réhydratation incrémentielle, progressive ou partielle pourrait être la clé pour rendre cette technique plus viable à l'avenir.

Diffuser le rendu côté serveur et réhydrater progressivement

Le rendu côté serveur a connu un certain nombre d'évolutions au cours des dernières années.

Le rendu côté serveur en streaming vous permet d'envoyer du code HTML par fragments que le navigateur peut afficher progressivement à mesure qu'il les reçoit. Cela peut permettre de transmettre le balisage plus rapidement à vos utilisateurs, ce qui accélère votre FCP. Dans React, les flux étant asynchrones dans renderToPipeableStream(), par rapport à renderToString() synchrone, la contre-pression est bien gérée.

La réhydratation progressive mérite également d'être envisagée (React l'a implémentée). Avec cette approche, les différentes parties d'une application affichée côté serveur sont "démarrées" au fil du temps, au lieu de l'approche courante actuelle qui consiste à initialiser l'ensemble de l'application en une seule fois. Cela peut aider à réduire la quantité de code JavaScript nécessaire pour rendre les pages interactives, car cela vous permet de différer la mise à niveau côté client des parties de la page de faible priorité afin d'éviter qu'elle ne bloque le thread principal, ce qui permet aux interactions utilisateur de se produire plus tôt après que l'utilisateur les a lancées.

La réhydratation progressive peut également vous aider à éviter l'un des pièges les plus courants de la réhydratation du rendu côté serveur : un arbre DOM affiché côté serveur est détruit, puis immédiatement recréé, le plus souvent parce que le rendu initial synchrone côté client nécessitait des données qui n'étaient pas tout à fait prêtes, souvent une Promise qui n'a pas encore été résolue.

Réhydratation partielle

La réhydratation partielle s'est avérée difficile à mettre en œuvre. Cette approche est une extension de la réhydratation progressive qui analyse des parties individuelles de la page (composants, vues ou arborescences) et identifie celles qui présentent peu d'interactivité ou aucune réactivité. Pour chacune de ces parties principalement statiques, le code JavaScript correspondant est ensuite transformé en références inertes et en fonctionnalités décoratives, ce qui réduit leur empreinte côté client à presque zéro.

L'approche de réhydratation partielle présente ses propres problèmes et compromis. Cela pose des défis intéressants pour la mise en cache, et la navigation côté client signifie que nous ne pouvons pas supposer que le code HTML affiché côté serveur pour les parties inertes de l'application est disponible sans chargement de page complet.

Rendu trisomorphe

Si les service workers sont une option pour vous, envisagez le rendu trisomorphe. Cette technique vous permet d'utiliser le rendu côté serveur en streaming pour les navigations initiales ou non JavaScript, puis de faire en sorte que votre service worker prenne en charge le rendu du code HTML pour les navigations après son installation. Cela permet de maintenir à jour les composants et les modèles mis en cache, et d'activer les navigations de style SPA pour afficher de nouvelles vues dans la même session. Cette approche fonctionne mieux lorsque vous pouvez partager le même code de création de modèles et de routage entre le serveur, la page client et le service worker.

Rendu trisomorphe, illustrant un navigateur et un service worker communiquant avec le serveur.

Considérations liées au SEO

Lorsqu'elles choisissent une stratégie de rendu Web, les équipes tiennent souvent compte de l'impact du SEO. Le rendu côté serveur est un choix populaire pour offrir une expérience "complète" que les robots d'exploration peuvent interpréter. Les robots d'exploration peuvent comprendre le code JavaScript, mais leur rendu est souvent limité. Le rendu côté client peut fonctionner, mais nécessite souvent des tests et des frais supplémentaires. Plus récemment, l'affichage dynamique est également devenu une option à envisager si votre architecture dépend fortement du code JavaScript côté client.

Conclusion

Lorsque vous choisissez une approche de rendu, mesurez et comprenez vos goulots d'étranglement. Déterminez si l'affichage statique ou l'affichage côté serveur peut vous aider à atteindre la plupart de vos objectifs. Vous pouvez tout à fait envoyer principalement du code HTML avec un minimum de JavaScript pour obtenir une expérience interactive. Voici une infographie pratique illustrant le spectre serveur-client :

Options de rendu et leurs compromis

Crédits

Merci à tous pour leurs commentaires et leur inspiration :

Jeffrey Posnick, Houssein Djirdeh, Shubhie Panicker, Chris Harrelson et Sebastian Markbåge.