Hydratation dans Next.js¶
Qu'est-ce que l'hydratation¶
L'hydratation est le processus par lequel React « prend en charge » un HTML statique déjà rendu par le serveur et y attache les gestionnaires d'événements et l'état JavaScript côté client.
Le cycle complet d'une page Next.js¶
1. Serveur → génère le HTML (SSR / SSG)
2. Navigateur → reçoit et affiche le HTML (page visible mais non interactive)
3. React → « hydrate » le HTML en y attachant les événements JS (page interactive)
Sans hydratation, la page serait visible mais aucun bouton, formulaire ou interaction ne fonctionnerait.
Pourquoi c'est important
L'hydratation est ce qui donne à Next.js le meilleur des deux mondes : un affichage rapide grâce au HTML du serveur, et une interactivité complète grâce à React côté client.
Comment fonctionne l'hydratation¶
Lors de l'hydratation, React parcourt le DOM existant et le compare au résultat que produirait son propre rendu virtuel. Si les deux correspondent parfaitement, React attache simplement ses événements sans recréer les éléments. C'est ce qu'on appelle la réconciliation.
HTML du serveur : <button>Compteur : 0</button>
React côté client : <button>Compteur : 0</button>
Correspondance — React attache onClick sans toucher au DOM
Server Components vs Client Components¶
Dans Next.js, tous les composants sont Server Components par défaut et ne s'hydratent pas — ils n'ont pas de JavaScript interactif. Seuls les Client Components (marqués "use client") passent par l'hydratation.
// Server Component — rendu serveur seulement, pas d'hydratation
export default function Page() {
return (
<main>
<h1>Compteur</h1>
<BoutonCompteur />
</main>
);
}
"use client";
import { useState } from "react";
export default function BoutonCompteur() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Compteur : {count}
</button>
);
}
Erreurs d'hydratation courantes¶
Une erreur d'hydratation survient quand le HTML produit par le serveur diffère de ce que React génèrerait côté client. React ne peut pas réconcilier les deux arbres et affiche une erreur.
1. Utiliser Date ou Math.random() dans le rendu¶
"use client";
export default function Horloge() {
// Date.now() retourne une valeur différente sur le serveur et le client
return <p>Heure actuelle : {new Date().toLocaleTimeString()}</p>;
}
"use client";
import { useState, useEffect } from "react";
export default function Horloge() {
const [heure, setHeure] = useState<string | null>(null);
useEffect(() => {
setHeure(new Date().toLocaleTimeString());
}, []);
if (!heure) return <p>Chargement...</p>;
return <p>Heure actuelle : {heure}</p>;
}
2. Accéder à window ou localStorage directement¶
"use client";
export default function Theme() {
// window n'existe pas côté serveur → erreur
const theme = window.localStorage.getItem("theme") ?? "clair";
return <div className={theme}>Contenu</div>;
}
"use client";
import { useState, useEffect } from "react";
export default function Theme() {
const [theme, setTheme] = useState("clair");
useEffect(() => {
const themeLocal = localStorage.getItem("theme") ?? "clair";
setTheme(themeLocal);
}, []);
return <div className={theme}>Contenu</div>;
}
3. HTML invalide (balises mal imbriquées)¶
Le navigateur corrige automatiquement certaines erreurs HTML, ce qui peut créer une divergence avec ce que React attend.
export default function Texte() {
return (
<p>
Paragraphe principal
<p>Sous-paragraphe</p> {/* HTML invalide */}
</p>
);
}
export default function Texte() {
return (
<div>
<p>Paragraphe principal</p>
<p>Sous-paragraphe</p>
</div>
);
}
4. Extensions de navigateur qui modifient le DOM¶
Certaines extensions (traducteurs, gestionnaires de mots de passe) modifient le DOM avant que React puisse l'hydrater, causant des faux positifs.
Faux positif
Si l'erreur d'hydratation disparaît en navigation privée ou en désactivant les extensions, c'est une extension qui cause le problème — pas votre code.
5. Données différentes entre serveur et client¶
"use client";
export default function ListeAleatoire() {
// Math.random() donne des résultats différents serveur/client
const items = [Math.random(), Math.random(), Math.random()];
return <ul>{items.map((n) => <li key={n}>{n}</li>)}</ul>;
}
Comment éviter les erreurs d'hydratation¶
| Situation | Solution recommandée |
|---|---|
| Valeur qui dépend du temps | Initialiser avec null, mettre à jour dans useEffect |
Accès à window / localStorage |
Toujours dans useEffect |
| Contenu différent selon l'utilisateur | Charger côté client avec useEffect + état |
| HTML invalide | Valider la structure des balises |
Utiliser suppressHydrationWarning¶
Pour les cas où la différence est intentionnelle et inoffensive (ex: timestamp formaté) :
export default function PiedDePage() {
return (
<footer>
<time suppressHydrationWarning>
{new Date().getFullYear()}
</time>
</footer>
);
}
À utiliser avec parcimonie
suppressHydrationWarning masque l'avertissement mais ne règle pas le problème. Réserver aux cas où la différence est vraiment sans conséquence.
Déboguer les erreurs d'hydratation¶
1. Lire le message d'erreur complet¶
Next.js affiche un message détaillé qui indique quelle partie du DOM diffère :
Error: Hydration failed because the initial UI does not match
what was rendered on the server.
Server: <button>Connexion</button>
Client: <button>Mon compte</button>
2. Identifier la source de la différence¶
Cherchez dans votre composant tout ce qui pourrait produire un résultat différent entre le serveur et le client :
- Appels à
Date,Math.random() - Accès à
window,document,localStorage,navigator - Données provenant de cookies ou de l'en-tête HTTP
- Logique conditionnelle basée sur l'environnement (
typeof window !== 'undefined')
3. Vérifier avec typeof window¶
"use client";
export default function Banner() {
const estMobile = typeof window !== "undefined" && window.innerWidth < 768;
return <div>{estMobile ? "Mobile" : "Bureau"}</div>;
}
"use client";
import { useState, useEffect } from "react";
export default function Banner() {
const [estMobile, setEstMobile] = useState(false);
useEffect(() => {
setEstMobile(window.innerWidth < 768);
}, []);
return <div>{estMobile ? "Mobile" : "Bureau"}</div>;
}
4. Utiliser les outils React DevTools¶
React DevTools (extension de navigateur) permet d'inspecter l'arbre de composants et d'identifier quel composant déclenche l'erreur d'hydratation.
5. Tester en désactivant JavaScript¶
Désactiver JavaScript dans le navigateur permet de voir exactement ce que le serveur envoie. Si ce rendu est incohérent avec le comportement attendu de React, c'est là que se trouve le problème.