Aller au contenu principal

Rendu côté serveur (SSR)

Le SSR Angular pré-rend les pages côté serveur avant de les envoyer au navigateur, améliorant les performances de chargement initial et le SEO.

Ajouter le SSR

ng add @angular/ssr

Fichiers générés :

  • src/app/app.config.server.ts — providers serveur
  • src/server.ts — point d'entrée Express

Configuration serveur

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

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

export const config = mergeApplicationConfig(appConfig, serverConfig);

Activer l'hydratation (app.config.ts)

import { provideClientHydration } from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(),
provideClientHydration(), // Évite les scintillements lors de l'hydratation
],
};

Protéger les API réservées au navigateur

Le serveur n'a pas accès à window, document ni localStorage. Utilisez isPlatformBrowser ou afterNextRender :

import { PLATFORM_ID, inject, afterNextRender } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

export class MyComponent {
private platformId = inject(PLATFORM_ID);

// Option A : isPlatformBrowser
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
const value = localStorage.getItem('cle');
}
}

// Option B : afterNextRender (recommandé, Angular 16+)
constructor() {
afterNextRender(() => {
// S'exécute uniquement dans le navigateur après le premier rendu
this.initBibliothequeExterne();
});
}
}

Transfert d'état (éviter le double fetch)

Sans transfert d'état, Angular re-récupère les données dans le navigateur que le serveur avait déjà récupérées.

import { TransferState, makeStateKey, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { of, tap } from 'rxjs';

const ALBUMS_KEY = makeStateKey<Album[]>('albums');

@Injectable({ providedIn: 'root' })
export class AlbumService {
private http = inject(HttpClient);
private transferState = inject(TransferState);

getAlbums() {
const cached = this.transferState.get(ALBUMS_KEY, null);
if (cached) {
this.transferState.remove(ALBUMS_KEY);
return of(cached);
}
return this.http.get<Album[]>('/api/albums').pipe(
tap(data => this.transferState.set(ALBUMS_KEY, data))
);
}
}

Construire et exécuter

# Construire les bundles navigateur + serveur
ng build

# Démarrer le serveur SSR
node dist/my-app/server/server.mjs

Structure de sortie :

dist/my-app/
├── browser/ ← Ressources statiques
└── server/
└── server.mjs ← Serveur Node

Vérifier que le SSR fonctionne

  1. Ouvrez http://localhost:4000
  2. Affichez la source de la page (Ctrl+U)
  3. Le HTML de la page doit contenir votre contenu rendu — et non juste <app-root></app-root>

Erreurs courantes

ProblèmeSolution
window is not definedProtéger avec isPlatformBrowser ou afterNextRender
Données récupérées deux foisUtiliser TransferState
Désaccord d'hydratationS'assurer que le serveur et le client rendent le même HTML
Crash localStorageDéplacer l'accès au stockage derrière une vérification de plateforme