Angular SSR : rendu côté serveur et hydration

🏷️ Front-end 📅 12/04/2026 20:00:00 👤 Mezgani said
Angular Ssr Hydration Performance Angular 17
Angular SSR : rendu côté serveur et hydration

Implémentez le Server-Side Rendering avec Angular Universal et maîtrisez l'hydration pour améliorer les performances et le SEO de votre application.

Pourquoi le SSR

Par défaut, Angular génère une application 100% client-side. Le navigateur reçoit un HTML quasi-vide et exécute le JavaScript pour construire la page. Cela pose deux problèmes :

  • SEO : les moteurs de recherche peuvent avoir du mal à indexer le contenu JavaScript
  • Performance (FCP/LCP) : l'utilisateur voit une page blanche jusqu'à ce que le JS soit chargé et exécuté

Le SSR résout ces problèmes en rendant le HTML complet côté serveur dès la première requête.

A retenir : Depuis Angular 17, le package @angular/ssr remplace Angular Universal et s'intègre directement dans le CLI Angular.

Installation de @angular/ssr

Pour ajouter le SSR à un projet existant :

# Ajouter @angular/ssr au projet
ng add @angular/ssr

# Ou lors de la création d'un nouveau projet
ng new my-app --ssr

Angular CLI génère automatiquement :

  • server.ts — le serveur Express
  • app.config.server.ts — configuration spécifique au serveur
  • Mise à jour de angular.json avec les cibles de build SSR

Configuration serveur

// app.config.ts (client)
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideClientHydration } from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideClientHydration(), // active l'hydration
  ]
};

// app.config.server.ts (serveur)
import { mergeApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';

const serverConfig = {
  providers: [
    provideServerRendering()
  ]
};

export const config = mergeApplicationConfig(appConfig, serverConfig);

L'hydration complète

Sans hydration, Angular détruisait et recréait entièrement le DOM au chargement client, causant un flash de contenu. Depuis Angular 16, provideClientHydration() permet à Angular de réutiliser le DOM existant rendu par le serveur.

// Avec hydration complète : Angular ne recrée pas le DOM
// Il "adopte" le HTML serveur et y attache les event listeners

// Vérifier que l'hydration fonctionne :
// Les DevTools Angular indiquent le statut d'hydration de chaque composant

// Désactiver l'hydration pour un composant spécifique (rare)
@Component({
  ...
  providers: [{ provide: HYDRATION_SKIP, useValue: true }]
})
Note : L'hydration complète est activée par défaut dans les nouveaux projets Angular 17+. Pour les migrations, ajoutez provideClientHydration() manuellement.

TransferState : partager les données

Un problème classique du SSR : les appels HTTP sont effectués côté serveur, puis répétés côté client. TransferState (ou le HTTP interceptor withHttpTransferCache) évite cette double requête :

// app.config.ts
import { provideClientHydration, withHttpTransferCache } from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
  providers: [
    provideClientHydration(
      withHttpTransferCache()
    ),
  ]
};

Avec withHttpTransferCache(), Angular sérialise les réponses HTTP dans le HTML SSR. Côté client, les mêmes URLs retournent immédiatement depuis le cache, sans nouvelle requête réseau.

Pièges courants du SSR

Le serveur Node.js n'a pas accès aux APIs du navigateur. Voici les erreurs fréquentes :

// ERREUR : window n'existe pas côté serveur
ngOnInit() {
  const width = window.innerWidth; // ReferenceError!
}

// SOLUTION : vérifier la plateforme
import { isPlatformBrowser } from '@angular/common';
import { PLATFORM_ID, inject } from '@angular/core';

ngOnInit() {
  if (isPlatformBrowser(inject(PLATFORM_ID))) {
    const width = window.innerWidth; // OK
  }
}
  • Ne jamais accéder à window, document, localStorage directement
  • Toujours vérifier avec isPlatformBrowser() ou afterNextRender()
  • Utiliser afterNextRender() pour le code qui doit s'exécuter uniquement côté client
  • Attention aux librairies tierces qui accèdent au DOM directement
// afterNextRender() : s'exécute uniquement côté client après le rendu
import { afterNextRender } from '@angular/core';

export class ChartComponent {
  constructor() {
    afterNextRender(() => {
      // Ici on peut accéder à window, document, etc.
      this.initChart();
    });
  }
}