Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Hydratation dans Angular

L'hydratation est un processus essentiel lorsque vous utilisez le rendu côté serveur (SSR) avec Angular. Elle permet de transformer le HTML statique généré par le serveur en une application Angular pleinement interactive, sans avoir à tout reconstruire côté client.

Dans ce tutoriel, nous allons découvrir ce qu'est l'hydratation, pourquoi elle est importante pour vos applications Angular, et comment la configurer correctement pour bénéficier de ses avantages.

Comprendre l'hydratation avec un exemple concret

Imaginez un livre de coloriage avec des dessins déjà tracés : vous recevez les contours, mais les pages ne sont pas encore animées. Pour que le livre devienne interactif, il faut "hydrater" ces dessins avec des couleurs et des animations.

L'hydratation fonctionne de la même manière dans Angular :

  1. Le serveur génère le HTML : Le contenu est déjà là, visible pour l'utilisateur
  2. Le navigateur affiche le HTML : L'utilisateur voit immédiatement le contenu
  3. Angular "hydrate" la page : Angular connecte le HTML existant à son code JavaScript pour le rendre interactif

Hydratation

CONCEPT CLÉ

L'hydratation permet à Angular de réutiliser le DOM déjà présent plutôt que de tout reconstruire depuis zéro, ce qui améliore considérablement les performances.

Pourquoi l'hydratation est importante

Quand on utilise le SSR avec Angular, le serveur génère le HTML de l'application avant de l'envoyer au navigateur. Cela signifie que l'utilisateur voit plus rapidement du contenu réel, ce qui est excellent pour l'expérience utilisateur et pour le référencement (SEO).

Mais ce n'est pas tout : une fois que le HTML est chargé dans le navigateur, il faut transformer cette page statique générée par le serveur en une application Angular vivante (avec les composants, les interactions, les événements). C'est là qu'intervient l'hydratation.

L'hydratation = le processus par lequel Angular "reprend" le HTML rendu par le serveur, le "connecte" à son code client et continue à fonctionner comme si l'application avait été démarrée seulement côté client.

Avantages concrets

AVANTAGES

  • Performance améliorée : Le HTML est déjà généré, l'utilisateur voit le contenu plus vite (amélioration des métriques comme le LCP, CLS)
  • Pas de re-rendu complet : On évite un re-rendu côté client qui pourrait provoquer un clignotement (flicker) ou un déplacement de mise en page (layout shift)
  • Meilleur SEO : Les moteurs de recherche peuvent indexer rapidement une page déjà rendue côté serveur, et l'hydratation assure que l'application devienne interactive ensuite

Comprendre les métriques de performance (Core Web Vitals)

Quand on parle de performance web, on mesure l'expérience utilisateur avec des indicateurs appelés Core Web Vitals (Vitals Essentiels du Web). Ces métriques permettent de mesurer objectivement si une page web est rapide et agréable à utiliser.

Qu'est-ce que les Core Web Vitals ?

Imaginez que vous évaluez un restaurant : vous regardez combien de temps vous attendez avant d'avoir votre plat (LCP), à quel point c'est réactif quand vous appelez le serveur (FID), et si les plats ne se déplacent pas sur la table (CLS). C'est exactement ce que font les Core Web Vitals pour votre site web !

Ces métriques sont importantes car :

  • Google les utilise pour le référencement : Un site avec de bonnes métriques est mieux classé dans les résultats de recherche
  • Elles mesurent la vraie expérience utilisateur : Pas seulement la vitesse technique, mais ce que ressent réellement l'utilisateur

Les trois métriques principales

Core Web Vitals

1. LCP (Largest Contentful Paint) - Le temps d'affichage du contenu principal

En langage simple : C'est le temps qu'il faut pour que l'élément le plus important de votre page (une image, un titre, un paragraphe) soit visible à l'écran.

Exemple concret : Sur une page d'actualité, l'LCP pourrait être le temps d'affichage de la grande photo d'article en haut de la page.

Avec l'hydratation :

  • Sans hydratation : Le serveur envoie du HTML, mais Angular doit tout reconstruire. L'élément principal n'apparaît que quand Angular a fini de charger.
  • Avec hydratation : Le HTML est déjà là, l'élément principal est visible immédiatement. Angular connecte simplement le code sans tout recréer, donc c'est beaucoup plus rapide !

Valeur cible : Moins de 2,5 secondes est considéré comme bon.

2. FID (First Input Delay) - Le délai avant la première interaction

En langage simple : C'est le temps entre le moment où l'utilisateur clique (ou tape) sur quelque chose et le moment où l'application réagit vraiment.

Exemple concret : Vous cliquez sur un bouton "Ajouter au panier", mais rien ne se passe pendant 1 seconde. C'est un mauvais FID. Avec un bon FID, le bouton réagit immédiatement.

Avec l'hydratation :

  • Sans hydratation : Angular doit d'abord détruire le HTML statique, reconstruire toute l'application, puis connecter les événements. Pendant ce temps, vos clics sont "perdus" ou ignorés.
  • Avec hydratation : Le HTML existe déjà, Angular le connecte rapidement aux événements. Votre application devient interactive beaucoup plus tôt, donc le FID est bien meilleur !

Valeur cible : Moins de 100 millisecondes (0,1 seconde) est considéré comme bon.

EVENT REPLAY

Avec l'Event Replay d'Angular, même si vous cliquez avant que l'hydratation soit terminée, vos actions sont enregistrées et "rejouées" une fois que tout est prêt. C'est comme si l'application vous attendait !

3. CLS (Cumulative Layout Shift) - Le déplacement cumulatif de la mise en page

En langage simple : C'est la mesure de tous les "sauts" ou "décalages" visuels qui se produisent pendant le chargement de la page. Un bon CLS signifie que rien ne bouge inopinément.

Exemple concret : Vous commencez à lire un article, mais soudainement, une publicité charge et pousse tout le texte vers le bas. Vous avez perdu votre place ! C'est un mauvais CLS.

Avec l'hydratation :

  • Sans hydratation : Angular détruit le HTML statique et le reconstruit. Cela peut causer un "clignotement" où la page disparaît puis réapparaît, ou où les éléments se déplacent. C'est très frustrant pour l'utilisateur !
  • Avec hydratation : Angular réutilise le HTML existant. Pas de destruction, pas de reconstruction, donc aucun déplacement ! Tout reste exactement à la même place. C'est une expérience beaucoup plus fluide.

Valeur cible : Moins de 0,1 est considéré comme bon (0 signifie aucun déplacement).

Impact sur les performances et le SEO

L'hydratation améliore ces métriques de plusieurs façons :

  1. Évite le travail supplémentaire : Au lieu de détruire et recréer tous les éléments du DOM, Angular réutilise ce qui existe déjà. C'est comme réparer une voiture au lieu d'en construire une nouvelle !

  2. Réduit le clignotement visuel : Sans hydratation, vous pouvez voir la page "clignoter" ou "sauter" quand Angular reconstruit tout. Avec l'hydratation, la transition est invisible.

  3. Améliore le référencement : Google utilise ces métriques pour classer les sites. De meilleures métriques = meilleur classement dans les résultats de recherche.

Avec et sans hydratation

SANS HYDRATATION

Sans l'hydratation activée, les applications Angular avec SSR vont détruire et re-rendre le DOM de l'application, ce qui peut provoquer un clignotement visible de l'interface. Ce re-rendu peut négativement impacter les Core Web Vitals comme le LCP et causer un décalage de mise en page (CLS).

AVEC HYDRATATION

L'hydratation permet de réutiliser le DOM existant et évite le clignotement. C'est pourquoi elle améliore directement les métriques LCP, FID et CLS, et par conséquent le référencement de votre site.

Comment ça fonctionne dans Angular

Voici les grandes étapes du processus d'hydratation :

1. Le serveur génère le HTML

Le serveur Angular crée le HTML de l'application via le SSR. Tous les composants sont rendus et le HTML final est prêt.

2. Le navigateur reçoit et affiche le HTML

Le navigateur reçoit ce HTML, l'affiche immédiatement. L'utilisateur voit le contenu, mais la page n'est pas encore interactive.

3. Angular démarre avec l'hydratation

Angular côté client démarre avec l'hydratation activée : il réutilise autant que possible le DOM déjà présent plutôt que tout reconstruire.

4. L'application devient interactive

Une fois l'hydratation terminée, l'application est pleinement interactive (événements, composants dynamiques, etc.).

EVENT REPLAY

Angular propose un mécanisme appelé Event Replay, qui capte les interactions utilisateurs faites avant que l'hydratation ne soit terminée, puis les "rejoue" une fois que tout est prêt. Ce qui améliore considérablement l'expérience utilisateur.

Comment activer l'hydratation dans Angular

Bonne nouvelle : si vous avez utilisé le CLI Angular pour ajouter le SSR via ng add @angular/ssr, l'hydratation est souvent déjà pré-configurée.

Sinon, voilà comment faire manuellement :

Configuration de base

Pour activer l'hydratation dans votre application, vous devez ajouter provideClientHydration() dans la configuration de votre application :

ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideClientHydration } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';

bootstrapApplication(AppComponent, {
  providers: [
    ...appConfig.providers,
    provideClientHydration()
  ]
});

Configuration dans app.config.ts

Si vous utilisez app.config.ts, voici comment configurer l'hydratation :

ts
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideClientHydration } from '@angular/platform-browser';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideHttpClient(),
    provideClientHydration()
  ]
};

IMPORTANT

Le même provideClientHydration() doit être présent dans la version serveur et client si votre setup est personnalisé.

Bonnes pratiques et points d'attention

Assurez-vous que le HTML est identique

Le HTML généré côté serveur et côté client doit être identique (structure, balises, espaces blancs, commentaires). Sinon, des erreurs de "mismatch" peuvent apparaître.

ts
// ❌ ÉVITER : Manipulation directe du DOM qui peut causer des différences
@Component({
  template: `<div id="user-list"></div>`,
})
export class BadComponent {
  constructor() {
    // This can cause hydration mismatch
    document.getElementById('user-list')!.innerHTML = '<p>Users</p>';
  }
}

// ✅ BON : Utiliser la liaison de données Angular
@Component({
  template: `<div><p>{{ message }}</p></div>`,
})
export class GoodComponent {
  message = 'Users';
}

Évitez les manipulations directes du DOM

Certains usages de manipulation directe du DOM (avec document, innerHTML, etc.) peuvent bloquer l'hydratation — car Angular ne retrouve pas la structure attendue.

Si vous devez manipuler le DOM, utilisez le hook afterNextRender() introduit avec Angular 17. Ce hook est spécialement conçu pour les manipulations DOM post-hydratation.

Pourquoi afterNextRender() ?

afterNextRender() est un nouveau hook introduit avec Angular 17 (API Signals) qui :

  • S'exécute après le prochain cycle de rendu du navigateur : Garantit que l'hydratation est terminée
  • Le DOM est définitivement stable : Plus de risque de perturber l'hydratation
  • Plus fiable pour manipuler le DOM : Idéal pour mesurer des dimensions (LCP), gérer le scroll, etc.
  • Idéal pour le travail DOM post-hydratation : Angular a déjà hydraté le DOM, attaché les listeners, et stabilisé les signaux

BONNE PRATIQUE

afterNextRender() est préférable à ngAfterViewInit car il garantit que l'hydratation est complètement terminée et évite les erreurs courantes liées au cycle de détection de changement.

Exemple correct avec afterNextRender()

ts
import { Component, afterNextRender, viewChild, ElementRef } from '@angular/core';

@Component({
  selector: 'app-my-component',
  template: `<div #content></div>`,
})
export class MyComponent {
  content = viewChild<ElementRef<HTMLDivElement>>('content');

  constructor() {
    // Execute DOM manipulation after the next render cycle
    // This guarantees that hydration is complete and DOM is stable
    afterNextRender(() => {
      const element = this.content()?.nativeElement;
      if (element) {
        // Safe DOM manipulation after hydration
        // Angular has already:
        // - hydrated the DOM
        // - attached event listeners
        // - stabilized signals
        // - finished post-hydration rendering
        element.textContent = 'Hydrated content';
      }
    });
  }
}

Pourquoi c'est mieux ?

Avec afterNextRender() :

  • Angular a déjà hydraté le DOM : Tous les éléments sont connectés au code Angular
  • Les listeners sont attachés : Les événements fonctionnent correctement
  • Les signaux sont stabilisés : Toutes les valeurs sont à jour
  • Le rendu post-hydratation est fini : Le DOM est dans son état final
  • Aucun risque de perturber l'hydratation : La manipulation se fait après, pas pendant

AVEC NGAFTERVIEWINIT

Avec ngAfterViewInit, il y a un risque que l'hydratation ne soit pas complètement terminée, ce qui peut causer des erreurs ou des comportements inattendus. afterNextRender() garantit que tout est prêt.

Exclure des composants de l'hydratation

Si un composant pose problème pour l'hydratation, vous pouvez utiliser ngSkipHydration pour l'exclure du processus — mais c'est une solution de secours, mieux vaut corriger le composant.

Il existe deux façons d'utiliser ngSkipHydration :

Méthode 1 : Dans le template (attribut)

Vous pouvez utiliser ngSkipHydration comme attribut directement dans le template :

ts
import { Component } from '@angular/core';

@Component({
  template: `
    <div>
      <app-problematic-component ngSkipHydration />
    </div>
  `,
})
export class ParentComponent {
  // Component logic
}

Méthode 2 : Comme host binding

Vous pouvez également définir ngSkipHydration comme host binding dans la configuration du composant. Cela permet d'exclure tout le composant de l'hydratation :

ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-example',
  // Setting ngSkipHydration as a host binding
  // This excludes the entire component from hydration
  host: { ngSkipHydration: 'true' },
  template: `
    <div>
      <!-- This entire component will skip hydration -->
      <p>Content that won't be hydrated</p>
    </div>
  `,
})
export class ExampleComponent {
  // Component logic
}

QUAND UTILISER CHAQUE MÉTHODE

  • Attribut dans le template : Utilisez cette méthode quand vous voulez exclure un composant enfant spécifique depuis le composant parent
  • Host binding : Utilisez cette méthode quand vous voulez que le composant lui-même soit toujours exclu de l'hydratation, peu importe où il est utilisé

ATTENTION

ngSkipHydration est une solution temporaire. Essayez toujours de corriger le problème à la source plutôt que d'exclure le composant de l'hydratation. L'hydratation améliore les performances et l'expérience utilisateur, donc évitez de l'exclure sauf en cas de nécessité absolue.

Vérifiez vos dépendances

Assurez-vous que vos services et dépendances fonctionnent correctement dans un environnement SSR :

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

export class MyService {
  private platformId = inject(PLATFORM_ID);
  
  // Method to safely access browser-only APIs
  // Checks if code is running in browser before accessing window
  getWindow(): Window | null {
    if (isPlatformBrowser(this.platformId)) {
      return window;
    }
    return null;
  }
}

Pour plus d'informations, voir l'article la compatibilité client/serveur.

Hydratation incrémentale (Incremental Hydration)

Angular 19+ introduit l'hydratation incrémentale, qui permet d'hydrater progressivement différentes parties de votre application plutôt que tout d'un coup.

Activer l'hydratation incrémentale

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

bootstrapApplication(AppComponent, {
  providers: [
    provideClientHydration(withIncrementalHydration())
  ]
});

Utilisation avec @defer

L'hydratation incrémentale s'intègre parfaitement avec la directive @defer :

ts
@Component({
  template: `
    <header>Tableau de bord</header>
    
    @defer (hydrate on viewport) {
      <app-user-statistics />
    } @placeholder {
      <div>Chargement des statistiques...</div>
    }
  `,
})
export class DashboardComponent {
  // Component logic
}

Dans cet exemple, le composant UserStatisticsComponent sera hydraté seulement quand il entre dans le viewport, améliorant ainsi les performances initiales.

Vérification et débogage

Pour vérifier que l'hydratation fonctionne correctement :

  1. Activez le mode SSR dans votre application :

    bash
    ng serve --ssr
  2. Vérifiez dans les DevTools : Le HTML devrait être présent dès le chargement initial, avant même que le JavaScript ne soit chargé

  3. Surveillez les erreurs de mismatch : Angular vous avertira dans la console s'il détecte des différences entre le rendu serveur et client