Découvrez le Route Render Mode d'Angular 19 : configurez SSR, CSR et Prerender par route dans app.routes.server.ts pour optimiser SEO et performance.
Le probleme avant Angular 19
Avant Angular 19, la strategie de rendu etait globale et binaire. Soit une application etait entierement rendue cote serveur (SSR via Angular Universal), soit elle etait entierement rendue cote client (CSR classique). Il n'existait aucun moyen natif de dire : "cette route utilise le SSR, cette autre le CSR, et cette troisieme est pre-generee".
Ce manque de granularite posait des problemes concrets :
- Une page marketing simple (statique) recevait le meme traitement SSR qu'un tableau de bord dynamique, gaspillant des ressources serveur.
- Un espace personnel authentifie (dashboard) utilisant le SSR global exposait des risques de fuites de donnees ou de collisions de sessions.
- Le Prerender (generation statique) n'etait applicable qu'a l'ensemble du site ou via des configurations externes complexes.
- Les equipipes devaient souvent recourir a des hacks : detection du navigateur dans les services, guards specifiques, configurations Nginx, etc.
La solution arrive avec Angular 19 sous la forme du fichier app.routes.server.ts et de l'enum RenderMode. Ce mecanisme s'integre directement dans le systeme de routing Angular sans necessiter de bibliotheques tierces.
Les 3 modes de rendu
Angular 19 introduit l'enum RenderMode qui expose trois valeurs correspondant a trois strategies de rendu distinctes. Chacune repond a un besoin different en termes de performance, de SEO et de comportement utilisateur.
RenderMode.Prerender — Generation statique
Le mode Prerender genere le HTML de la route au moment du build. Le fichier HTML produit est servi directement par le serveur web (Nginx, CDN) sans aucun calcul supplementaire. C'est le mode le plus rapide possible en termes de Time To First Byte (TTFB).
- Le HTML est genere une seule fois, lors du build.
- Aucun Node.js ne tourne en production pour ces routes.
- Parfait pour des pages dont le contenu change rarement.
- Compatible avec l'hydration Angular pour redonner l'interactivite au client.
RenderMode.Server — Rendu serveur a la demande (SSR)
Le mode Server execute le rendu Angular cote serveur a chaque requete HTTP entrante. Le serveur Node.js genere un HTML complet et l'envoie au navigateur. Ce mode est ideal pour les pages dont le contenu est dynamique et depend de la requete (utilisateur connecte, parametres d'URL variables, donnees en temps reel).
- HTML genere a chaque requete par le serveur Node.js.
- Le contenu peut etre personnalise par utilisateur ou par contexte.
- Meilleur pour le SEO des pages a contenu dynamique.
- Necessite un serveur Node.js actif en production.
RenderMode.Client — Rendu cote client (CSR)
Le mode Client desactive completement le rendu serveur pour la route concernee. Le navigateur recoit une page HTML minimale et Angular prend en charge l'integralite du rendu en JavaScript. C'est le comportement classique d'une Single Page Application.
- Aucun rendu cote serveur : le HTML initial est vide.
- Ideal pour les interfaces interactives complexes (dashboards, editeurs).
- Evite les problemes de SSR sur les zones authentifiees.
- Ne necessite pas de serveur Node.js pour le rendu.
| Mode | Rendu | TTFB | SEO | Cas d'usage |
|---|---|---|---|---|
Prerender |
Build time | Tres rapide | Excellent | Pages marketing, blog, landing pages |
Server |
A chaque requete | Moyen | Tres bon | Fiches produit, contenu dynamique indexable |
Client |
Navigateur | Lent (JS) | Faible | Dashboard, espace personnel, editeurs |
Configuration dans app.routes.server.ts
La configuration du Route Render Mode se fait dans un fichier dedie : app.routes.server.ts. Ce fichier est specifique au contexte serveur et ne fait pas partie du bundle client. Il exporte un tableau serverRoutes de type ServerRoute[].
Voici comment installer et configurer Angular SSR avec le Route Render Mode :
# Ajouter SSR a un projet Angular existant
ng add @angular/ssr
# Cette commande cree automatiquement :
# - app.config.server.ts : configuration serveur
# - app.routes.server.ts : regles de rendu par route
# - server.ts : serveur Express/Node.js
Le fichier app.routes.server.ts contient la definition des regles de rendu. Chaque entree associe un chemin de route a un mode RenderMode :
// app.routes.server.ts
// Fichier exclusivement cote serveur — ne fait pas partie du bundle navigateur
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
// Page d'accueil : pre-generee au build pour un TTFB optimal
{
path: '',
renderMode: RenderMode.Prerender
},
// Pages marketing statiques : pre-generees pour le SEO et la rapidite
{
path: 'a-propos',
renderMode: RenderMode.Prerender
},
{
path: 'contact',
renderMode: RenderMode.Prerender
},
// Catalogue produits : rendu serveur a chaque requete (contenu dynamique)
{
path: 'produits',
renderMode: RenderMode.Server
},
// Fiche produit individuelle : SSR avec parametres dynamiques
{
path: 'produits/:id',
renderMode: RenderMode.Server
},
// Dashboard utilisateur : CSR uniquement (zone privee, pas de SSR necessaire)
{
path: 'dashboard',
renderMode: RenderMode.Client
},
{
path: 'dashboard/**',
renderMode: RenderMode.Client
},
// Regle de secours : toutes les autres routes utilisent SSR par defaut
{
path: '**',
renderMode: RenderMode.Server
}
];
serverRoutes est important. Angular evalue les regles dans l'ordre et applique la premiere correspondance trouvee. Placez toujours la regle generique '**' en dernier.
Il faut egalement declarer ce fichier dans la configuration du serveur pour qu'Angular le prenne en compte :
// app.config.server.ts
// Configuration Angular cote serveur — complete app.config.ts
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { provideServerRoutesConfig } from '@angular/ssr';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';
// Configuration specifique au contexte serveur
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
// Enregistre les regles de rendu par route
provideServerRoutesConfig(serverRoutes)
]
};
// Fusionne avec la configuration client pour obtenir la config complete serveur
export const config = mergeApplicationConfig(appConfig, serverConfig);
Prerender avec routes parametriques
Lorsqu'une route contient des parametres dynamiques (ex: articles/:slug), Angular a besoin de connaitre la liste exacte des chemins a pre-generer. On utilise la propriete getPrerenderParams pour fournir cette liste :
// app.routes.server.ts
// Pre-generation de routes dynamiques avec parametres
import { RenderMode, ServerRoute } from '@angular/ssr';
import { inject } from '@angular/core';
import { ArticleService } from './services/article.service';
export const serverRoutes: ServerRoute[] = [
// Pre-generation de toutes les fiches articles connues au moment du build
{
path: 'articles/:slug',
renderMode: RenderMode.Prerender,
// Fonction qui retourne la liste des parametres a generer
// Angular appellera cette fonction une seule fois au build
async getPrerenderParams() {
// Injection disponible dans ce contexte async
const articleService = inject(ArticleService);
// Charge la liste de tous les articles depuis l'API ou un fichier JSON
const articles = await articleService.getAllSlugs();
// Retourne un tableau d'objets parametres — un objet par URL a generer
return articles.map(slug => ({ slug }));
// Resultat : [{ slug: 'intro-angular' }, { slug: 'rxjs-guide' }, ...]
}
},
// Regle de secours
{
path: '**',
renderMode: RenderMode.Server
}
];
getPrerenderParams est appele une seule fois au moment du ng build. Les donnees qu'il retourne doivent etre stables et disponibles au build. Pour des contenus modifies frequemment, preferez RenderMode.Server.
Cas d'usage par mode
Le choix du bon mode de rendu depend avant tout de la nature du contenu et du comportement attendu. Voici trois architectures types representant les scenarios les plus courants.
Site e-commerce : mix des 3 modes
Un site e-commerce illustre parfaitement l'utilite du Route Render Mode. Chaque zone du site a des besoins distincts :
// app.routes.server.ts — Site e-commerce avec 3 modes combines
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
// Pages institutionnelles : Prerender (contenu fixe, SEO prioritaire)
{ path: '', renderMode: RenderMode.Prerender },
{ path: 'a-propos', renderMode: RenderMode.Prerender },
{ path: 'livraison', renderMode: RenderMode.Prerender },
{ path: 'cgv', renderMode: RenderMode.Prerender },
// Catalogue et fiches produit : SSR (contenu dynamique indexable)
// Le stock, le prix et les disponibilites sont en temps reel
{ path: 'catalogue', renderMode: RenderMode.Server },
{ path: 'catalogue/:id', renderMode: RenderMode.Server },
{ path: 'recherche', renderMode: RenderMode.Server },
// Espace client : CSR uniquement (zone privee, non indexable)
// Le SSR cote serveur ne doit jamais acceder aux donnees de session utilisateur
{ path: 'mon-compte', renderMode: RenderMode.Client },
{ path: 'mon-compte/**', renderMode: RenderMode.Client },
{ path: 'panier', renderMode: RenderMode.Client },
{ path: 'commande/**', renderMode: RenderMode.Client },
// Fallback SSR pour toutes les autres routes
{ path: '**', renderMode: RenderMode.Server }
];
Application SaaS : dashboard CSR, landing SSR/Prerender
Dans une application SaaS, la landing page et les pages de documentation doivent etre indexables et rapides, tandis que l'application metier (dashboard) doit etre entierement reactive cote client :
// app.routes.server.ts — Application SaaS
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
// Landing pages et pages publiques : Prerender pour performance maximale
{ path: '', renderMode: RenderMode.Prerender },
{ path: 'fonctionnalites', renderMode: RenderMode.Prerender },
{ path: 'tarifs', renderMode: RenderMode.Prerender },
// Documentation : SSR (contenu evolue frequemment, indexation importante)
{ path: 'docs', renderMode: RenderMode.Server },
{ path: 'docs/:section', renderMode: RenderMode.Server },
// Application metier complete : CSR
// Toute la logique metier s'execute dans le navigateur
// Aucun etat serveur partage entre utilisateurs
{ path: 'app', renderMode: RenderMode.Client },
{ path: 'app/**', renderMode: RenderMode.Client },
// Page de login : CSR (interaction pure, pas besoin d'indexation)
{ path: 'login', renderMode: RenderMode.Client },
{ path: 'register', renderMode: RenderMode.Client },
{ path: '**', renderMode: RenderMode.Server }
];
Blog / site editorial : Prerender dominant
Pour un blog ou un site de contenu, le Prerender est le mode ideal pour la quasi-totalite des pages. Le SSR reste utile pour les pages de recherche ou les flux RSS dynamiques :
// app.routes.server.ts — Blog et site editorial
import { RenderMode, ServerRoute } from '@angular/ssr';
import { inject } from '@angular/core';
import { BlogService } from './services/blog.service';
export const serverRoutes: ServerRoute[] = [
// Page d'accueil : Prerender (liste des derniers articles)
{ path: '', renderMode: RenderMode.Prerender },
// Pages statiques : Prerender
{ path: 'a-propos', renderMode: RenderMode.Prerender },
{ path: 'auteurs', renderMode: RenderMode.Prerender },
// Articles de blog : Prerender avec liste dynamique des slugs
{
path: 'articles/:slug',
renderMode: RenderMode.Prerender,
async getPrerenderParams() {
const blogService = inject(BlogService);
// Recupere tous les slugs depuis le CMS au moment du build
const slugs = await blogService.getAllSlugs();
return slugs.map(slug => ({ slug }));
}
},
// Recherche : SSR (resultats dependent du parametre de requete)
{ path: 'recherche', renderMode: RenderMode.Server },
{ path: '**', renderMode: RenderMode.Server }
];
Impact SEO et performance selon le mode
Le choix du mode de rendu a des consequences directes sur deux metriques critiques : le SEO et les Core Web Vitals. Comprendre ces impacts permet de faire des choix eclaires.
SEO : ce que voient les robots d'indexation
Les robots d'indexation (Googlebot, Bingbot) preferent recevoir un HTML complet et pre-rempli. Voici ce que chaque mode leur envoie :
- Prerender : HTML statique complet, servi instantanement. Googlebot lit le contenu sans aucun effort de rendu JavaScript. Score SEO maximum.
- Server (SSR) : HTML complet genere a la volee. Googlebot recoit egalement un contenu complet mais avec un TTFB plus eleve. Tres bon pour le SEO.
- Client (CSR) : HTML minimal, Googlebot doit executer JavaScript pour voir le contenu. Google indexe le CSR mais avec un delai (crawl budget). Deconseille pour les pages strategiques SEO.
Core Web Vitals : impact par mode
Les Core Web Vitals (LCP, FID/INP, CLS) sont directement affectes par le mode de rendu :
| Metrique | Prerender | Server (SSR) | Client (CSR) |
|---|---|---|---|
| LCP (Largest Contentful Paint) | Excellent (HTML statique) | Bon (dependant TTFB) | Faible (attend JS) |
| INP (Interaction to Next Paint) | Bon apres hydration | Bon apres hydration | Excellent (reactif) |
| CLS (Cumulative Layout Shift) | Excellent (layout stable) | Bon | Variable (chargements JS) |
| TTFB (Time To First Byte) | Tres rapide (<50ms) | Moyen (50-300ms) | Rapide (HTML vide) |
Hydration et transfert d'etat
Avec Prerender et Server, Angular utilise l'hydration pour "redonner vie" au HTML statique cote client. Depuis Angular 17+, l'hydration non destructive est activee par defaut, ce qui evite le re-rendu complet du DOM.
// app.config.ts
// Activation de l'hydration non destructive (active par defaut depuis Angular 17)
import { ApplicationConfig } from '@angular/core';
import { provideClientHydration } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
// Active l'hydration : Angular reutilise le HTML SSR/Prerender
// sans detruire et recreer le DOM — eliminant le "flash de contenu"
provideClientHydration()
]
};
Transfert d'etat HTTP (HttpTransferCache)
Un probleme classique du SSR est la double requete HTTP : la requete est faite cote serveur pour generer le HTML, puis refaite cote client lors de l'hydration. Angular resout ce probleme avec le HttpTransferCache :
// app.config.ts
// Activation du cache de transfert HTTP pour eviter les doubles requetes SSR/Client
import { ApplicationConfig } from '@angular/core';
import { provideClientHydration,
withHttpTransferCache } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { provideHttpClient,
withFetch } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
// withFetch : utilise l'API Fetch native plutot que XMLHttpRequest
provideHttpClient(withFetch()),
// withHttpTransferCache : les reponses HTTP du SSR sont serialisees dans le HTML
// et reutilisees cote client — eliminant les doubles requetes
provideClientHydration(withHttpTransferCache())
]
};
Combinaison avec @defer
Le Route Render Mode se combine naturellement avec le bloc @defer introduit dans Angular 17. Cette combinaison permet d'optimiser finement le chargement de chaque partie d'une page selon son mode de rendu.
@defer avec SSR : chargement progressif
Dans une route en mode RenderMode.Server, on peut utiliser @defer pour charger les blocs secondaires uniquement apres l'hydration, reduisant la charge de rendu serveur :
<!-- produit-detail.component.html -->
<!-- Route en RenderMode.Server : le contenu principal est rendu cote serveur -->
<!-- Bloc principal : rendu par le serveur, visible immediatement -->
<section class="produit-hero">
<h1>{{ produit.nom }}</h1>
<p class="prix">{{ produit.prix | currency:'EUR' }}</p>
<p class="description">{{ produit.description }}</p>
</section>
<!-- Avis clients : charge de facon differee apres hydration -->
<!-- prefetch on idle : prefetch en arriere-plan quand le navigateur est disponible -->
@defer (on viewport; prefetch on idle) {
<app-avis-clients [produitId]="produit.id" />
} @placeholder {
<!-- Placeholder leger affiche pendant le chargement -->
<div class="avis-skeleton" aria-label="Chargement des avis...">
<div class="skeleton-line"></div>
<div class="skeleton-line"></div>
</div>
}
<!-- Produits similaires : charge uniquement quand visible dans le viewport -->
@defer (on viewport) {
<app-produits-similaires [categorieId]="produit.categorieId" />
} @loading {
<p>Chargement des suggestions...</p>
}
@defer avec Prerender : hydration selective
Avec RenderMode.Prerender, le HTML est genere au build. Les blocs @defer dans ce contexte permettent de ne pas inclure certains composants lourds dans la generation statique, tout en les chargeant cote client :
<!-- page-accueil.component.html -->
<!-- Route en RenderMode.Prerender -->
<!-- Contenu hero : inclus dans la generation statique -->
<section class="hero">
<h1>Bienvenue sur notre plateforme</h1>
<p>Decouvrez nos services innovants</p>
<a routerLink="/fonctionnalites" class="btn btn-primary">En savoir plus</a>
</section>
<!-- Widget meteo ou donnees en temps reel : ne peut pas etre pre-genere -->
<!-- on immediate : chargement des que le composant est rendu -->
@defer (on immediate) {
<app-widget-statistiques />
} @placeholder {
<div class="stats-placeholder">Chargement des statistiques...</div>
}
<!-- Section temoignages : charge au scroll pour ne pas bloquer le LCP -->
@defer (on viewport; prefetch on idle) {
<app-temoignages />
}
@defer avec CSR : optimisation du bundle initial
Dans une route RenderMode.Client (dashboard), @defer permet de diviser le bundle JavaScript en chunks charges a la demande, ameliorant le temps de chargement initial de l'application :
<!-- dashboard.component.html -->
<!-- Route en RenderMode.Client : tout est rendu dans le navigateur -->
<!-- Navigation principale : toujours presente -->
<app-dashboard-nav />
<!-- Graphiques complexes : lourds en JS, charges uniquement quand necessaire -->
@defer (on interaction) {
<!-- Ce composant et ses dependances (Chart.js, etc.) -->
<!-- sont telecharges uniquement quand l'utilisateur clique -->
<app-graphique-avance [data]="metriques()" />
} @placeholder {
<button class="btn btn-outline-primary">Afficher les graphiques</button>
} @loading (minimum 200ms) {
<div class="spinner-border" role="status">
<span class="sr-only">Chargement...</span>
</div>
}
<!-- Editeur riche : charge uniquement si la route enfant est activee -->
@defer (when showEditor()) {
<app-editeur-contenu />
}
RenderMode.Prerender, Angular evalue les blocs @defer en mode serveur. Par defaut, leur contenu n'est pas inclus dans le HTML pre-genere sauf si vous utilisez l'option hydrate on ... introduite dans Angular 19 pour l'hydration incrementale.
Accessibilite et responsive Bootstrap 4
Le mode de rendu choisi a des implications directes sur l'accessibilite et l'experience responsive. Voici les bonnes pratiques a appliquer selon chaque contexte.
Accessibilite avec SSR et Prerender
Avec le SSR et le Prerender, le HTML est disponible immediatement, ce qui beneficie aux lecteurs d'ecran qui n'ont pas besoin d'attendre l'execution JavaScript pour lire le contenu. Quelques points a verifier :
- S'assurer que les attributs ARIA (
aria-label,aria-describedby,role) sont correctement inclus dans le HTML genere cote serveur. - Verifier que les liens de navigation (
routerLink) sont renders comme de vrais elements<a href="...">dans le HTML SSR, pas uniquement apres hydration. - Tester le focus keyboard : les elements focusables doivent etre accessibles avant et apres l'hydration.
- Eviter les "flash" visuels lors de l'hydration qui peuvent desorienter les utilisateurs de lecteurs d'ecran.
Accessibilite avec CSR
En mode CSR, l'accessibilite repose entierement sur JavaScript. Les bonnes pratiques sont :
- Toujours fournir un
<main>avecid="main-content"et un lien "Aller au contenu" en premiere position. - Gerer les annonces ARIA lors des navigations de page (
aria-livesur la zone de titre). - S'assurer que le focus est replace correctement apres chaque navigation Angular (
Routerscroll strategy).
// app.config.ts
// Configuration du scroll et du focus pour l'accessibilite avec le Router Angular
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withInMemoryScrolling,
withRouterConfig } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(
routes,
// Repositionne le scroll en haut de page a chaque navigation
withInMemoryScrolling({ scrollPositionRestoration: 'top' }),
// Active le suivi du titre pour les lecteurs d'ecran
withRouterConfig({ onSameUrlNavigation: 'reload' })
)
]
};
Responsive Bootstrap 4 selon le mode de rendu
La grille Bootstrap 4 fonctionne de la meme facon quel que soit le mode de rendu, mais certaines precautions s'appliquent :
- En mode SSR/Prerender, ne pas utiliser
window.innerWidthouscreen.widthdans les composants (l'objetwindown'existe pas cote serveur). Utiliser les classes CSS Bootstrap responsives a la place. - Utiliser
isPlatformBrowser()pour conditionner le code specifique au navigateur dans les composants mixtes SSR/CSR. - Les classes
col-12 col-md-6 col-lg-4sont parfaitement compatibles avec le SSR car elles s'appliquent via CSS, pas JavaScript.
// composant utilise en mode SSR ou Prerender
// Bonne pratique : detecter la plateforme pour le code specifique navigateur
import { Component, OnInit, PLATFORM_ID, inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Component({
selector: 'app-carousel',
standalone: true,
template: `
<div class="row">
<!-- Classes Bootstrap responsives : fonctionnent cote serveur ET client -->
@for (item of items; track item.id) {
<div class="col-12 col-md-6 col-lg-4 mb-4">
<div class="card h-100">
<img [src]="item.image" class="card-img-top"
[alt]="item.titre" loading="lazy">
<div class="card-body">
<h3 class="card-title">{{ item.titre }}</h3>
</div>
</div>
</div>
}
</div>
`
})
export class CarouselComponent implements OnInit {
private platformId = inject(PLATFORM_ID);
items: { id: number; titre: string; image: string }[] = [];
ngOnInit(): void {
// Ce code s'execute cote serveur ET cote client
this.items = [/* donnees ... */];
// Ce bloc ne s'execute que dans le navigateur
// isPlatformBrowser evite les erreurs "window is not defined" cote serveur
if (isPlatformBrowser(this.platformId)) {
// Initialisation de bibliotheques qui dependent du DOM (ex: Swiper, Lightbox)
this.initSwiper();
}
}
private initSwiper(): void {
// Initialisation specifique navigateur
}
}
Conclusion
Le Route Render Mode d'Angular 19 est une avancee majeure qui met fin au choix binaire SSR global vs CSR global. En configurant le mode de rendu route par route dans app.routes.server.ts, vous pouvez optimiser independamment chaque zone de votre application : vitesse maximale avec Prerender pour les pages statiques, indexation dynamique avec SSR pour le contenu evolutif, et reactivite totale avec CSR pour les interfaces complexes.
La combinaison avec @defer, l'hydration non destructive et le HttpTransferCache complete cette approche pour produire des applications Angular a la fois performantes, accessibles et bien referencees. Commencez par auditer votre application route par route, identifiez le mode le plus adapte a chaque section, et appliquez la configuration progressivement.