Le rendu des applications d'entreprise : CSR, SSR ou les deux ?
ou pour une app ou React d'entreprise : quand le rendu serveur paie, le coût de l', , routes lazy, , .

Une équipe ajoute le rendu côté serveur à un tableau de bord bancaire interne. Chaque page exige un utilisateur connecté, aucun moteur de recherche ne la verra jamais, et il y a désormais un serveur à faire tourner, à patcher et à auditer. Le premier chargement est un peu plus rapide. La facture et la sont bien plus grandes.
Beaucoup d'applications d'entreprise accessibles après connexion n'ont pas besoin du rendu serveur. Certaines parties de la même organisation, si. La décision se prend page par page, pas pour toute l'entreprise.
Le CSR pour l'application, le SSR ou le prérendu pour les pages publiques
Le guide d'Angular est honnête sur les deux côtés. Le rendu côté client « a le modèle de développement le plus simple », et « le serveur n'a aucun travail à faire pour rendre une page au-delà de servir des assets JavaScript statiques ». Il « offre généralement de moins bonnes performances » et « peut nuire » au des pages indexées par les robots. Le rendu côté serveur « peut augmenter les coûts d'hébergement » et « exige d'écrire du code qui ne dépend pas strictement des API du navigateur ».
Image : tt-tech.io, adapté de web.dev, Rendering on the web, CC BY 4.0.
La ligne que je trace, c'est donc la connexion. Après elle (tableaux de bord, back office, traitement des dossiers), du CSR. Avant elle (pages produits, taux, recherche d'agences), prérendez ce qui est identique pour tout le monde et rendez côté serveur ce qui change à chaque requête. web.dev dit la même chose en termes généraux : « vous pouvez toujours choisir le rendu côté serveur pour certaines pages et pas pour d'autres ». Angular le permet route par route avec RenderMode.Client, Prerender et Server. Souvent, le site public et l'application sont tout simplement deux applications.
L'hydratation est le prix du SSR
Le HTML du serveur doit encore devenir une application. L'hydratation d'Angular réutilise le rendu par le serveur. Sans elle, Angular détruit ce DOM puis en refait le rendu, ce qui « peut provoquer un scintillement visible de l'interface ». Mais « les écouteurs d'événements ne sont attachés qu'une fois l'hydratation terminée », c'est pourquoi Angular fournit l'.
L' va plus loin : des blocs @defer avec des déclencheurs hydrate (idle, viewport, interaction) contrôlent quand Angular charge et hydrate cette partie, ce qui « peut produire des bundles initiaux plus petits ». Elle « est activée par défaut quand vous utilisez provideClientHydration() ». Cela vaut la peine sur une page publique avec un widget lourd. Ce n'est pas une raison d'ajouter du SSR à un tableau de bord.
@defer pour les widgets lourds
Les graphiques, les éditeurs de texte riche et les visionneuses PDF ont rarement leur place dans le premier . Les blocs @defer « réduisent la taille du bundle initial de votre application en différant le chargement du code qui n'est pas strictement nécessaire au rendu initial d'une page ».
Un piège propre à l'entreprise : les dépendances différées « doivent être standalone ». Le code qui vit encore dans un NgModule ne peut pas être différé. C'est votre coin qui décide de ce que vous pouvez charger en .
Dans une vieille base de code, c'est le coin legacy qui décide de ce que vous pouvez charger en lazy loading.
Des routes lazy qui suivent les équipes
Les routes lazy découpent le bundle là où vos équipes découpent déjà le code : une bibliothèque de fonctionnalité, une route, un fichier JavaScript. Angular note que les routes lazy retirent « de grandes portions de JavaScript du bundle initial », recommande le chargement immédiat « pour la ou les pages d'accueil principales », et prévient qu'un lazy loading profondément imbriqué « peut nuire fortement aux performances ».
Zoneless et OnPush par défaut
Deux valeurs par défaut ont changé ce qui déclenche le rendu dans Angular. Le zoneless « est le mode par défaut depuis Angular v21 ». « est la stratégie de détection de changements par défaut dans Angular (depuis la v22) ». Le rendu suit désormais des notifications : un lu dans un change, un input est défini, un écouteur du template s'exécute, ou du code appelle markForCheck(). Le code legacy qui modifiait un champ en attendant que s'en aperçoive a maintenant besoin de l'une de ces notifications.
Les React Server Components
Côté React, les Server Components sont rendus « à l'avance, avant le bundling, dans un environnement séparé de votre application client ou de votre serveur SSR ». Ils conviennent aux pages riches en contenu qui lisent leurs données sur le serveur. Ils viennent généralement avec les , qui déplacent une ligne de sécurité dans le dépôt frontend : leurs arguments « sont entièrement contrôlés par le client », et React vous demande de « vérifier que l'utilisateur connecté a le droit d'effectuer cette action ». Dans une application réglementée, cette fonction est un endpoint public, et doit être relue comme tel.
Sur Atlas Bank
Atlas Bank est rendue côté client, et c'est voulu : de la banque en ligne accessible après connexion, pas de SEO, pas de serveur Node. Elle est zoneless (l'AGENTS.md du dépôt dit « N'ajoutez jamais zone.js ») et chaque fonctionnalité est une route lazy dans apps/atlas/src/app/app.routes.ts, écrans legacy compris. Deux des cinq routes :
{
path: 'accounts',
loadChildren: () =>
import('@atlas/feature-accounts').then((m) => m.accountsRoutes),
},
{
path: 'transfers',
loadChildren: () =>
import('@atlas/legacy-transfers').then((m) => m.LegacyTransfersModule),
},Le NgModule legacy peut toujours être une route lazy ; il ne peut simplement pas se trouver dans un bloc @defer. Même le tableau de bord sur '' est lazy, contre l'avis d'Angular pour les pages d'accueil. Dans une petite démo, la différence est faible ; dans une vraie application, je la mesurerais.
Ce que j'ai laissé de côté, et pourquoi
- , et SSR en comme sujets à part. Des variantes propres à chaque framework sur le thème « envoyer moins de JavaScript ».
- . Une technique pour les sites de contenu, surtout connue via .
- contre réactivité fine. Un débat de frameworks qui change rarement une décision produit.
Dès lundi
- Listez vos routes et marquez chacune : accessible après connexion ou non. Seul le second groupe pose la question du SSR.
- Trouvez votre widget le plus lourd sous la ligne de flottaison. Entourez-le de
@defer (on viewport)et comparez le bundle initial. - Cherchez les composants qui comptent encore sur Zone.js pour remarquer un changement. C'est votre zoneless.
Ensuite : la partie 5, sur la performance.
Pour votre agent de code
Les règles de cet article, écrites pour un agent. Collez-les dans votre AGENTS.md, enregistrez-les comme , ou indiquez le fichier à votre agent.
# Rendering rules for enterprise apps Source: https://tt-tech.io/blog/enterprise-frontend-rendering-csr-ssr For Angular and React frontends. Add to AGENTS.md or save as a skill. Your project's own rules win where they differ. ## Rules 1. Decide rendering per page, not per app. The line is the login: pages behind it use client-side rendering (CSR) by default. 2. Do not add server-side rendering (SSR) to dashboards, back office or other pages behind a login by default. It adds a server to run, patch and audit. 3. For public pages, prerender what is the same for everyone and server-render what changes per request. In Angular, set it per route with `RenderMode.Client`, `RenderMode.Prerender` and `RenderMode.Server`. 4. Code that runs under SSR must not depend on browser APIs. Keep hydration on with `provideClientHydration()`, so Angular reuses the server DOM instead of re-rendering it. 5. Use incremental hydration (`@defer` with `hydrate` triggers such as idle, viewport or interaction) for a heavy widget on a public page. It is not a reason to add SSR. 6. Put heavy widgets below the fold (charts, rich-text editors, PDF viewers) in `@defer`, for example `@defer (on viewport)`. 7. Deferred dependencies must be standalone. Code that still lives in an `NgModule` cannot go in `@defer`; load it through a lazy route instead. 8. Split lazy routes along team lines: one feature library, one route, one chunk. Load primary landing pages eagerly, as Angular recommends, and avoid deeply nested lazy loading. 9. Never rely on Zone.js to notice a change. Trigger rendering through a signal read in the template, an input, a template listener, or `markForCheck()`. 10. Treat every React Server Function (`'use server'`) as a public endpoint. Its arguments are client-controlled: validate them and check that the logged-in user may perform the action. ## Before you say done - For every new or changed route, state whether it is behind the login and which render mode it uses. - When you add or move a `@defer` block or a lazy route, run the production build and report the initial bundle size before and after. - Search changed components for plain fields the template reads and code mutates. Make them signals, or name the notification that renders them. - Search the diff for `'use server'`. For each function, name where it validates its arguments and checks the user.
Sources
- Angular, Server-side and hybrid-rendering, documentation
- web.dev, Rendering on the web
- Angular, Hydration, documentation
- Angular, Incremental Hydration, documentation
- Angular, Deferred loading with @defer, documentation
- Angular, Lazy-loaded routes, documentation
- Angular, Angular without ZoneJS (Zoneless), documentation
- Angular, Skipping component subtrees, documentation
- React, Server Components, documentation
- React, 'use server' directive, documentation