Back

Next.js : Corriger l'erreur « Hydration failed because the initial UI does not match »

Next.js : Corriger l'erreur « Hydration failed because the initial UI does not match »

Si vous avez rencontré la redoutable erreur « Hydration failed because the initial UI does not match what was rendered on the server » dans votre application Next.js, vous n’êtes pas seul. Cette incompatibilité d’hydratation React est l’un des problèmes les plus courants auxquels les développeurs sont confrontés lors de la création d’applications rendues côté serveur. Clarifions les choses et corrigeons-la correctement.

Points clés à retenir

  • Les erreurs d’hydratation surviennent lorsque le serveur et le client génèrent des HTML différents
  • Les API spécifiques au navigateur, les valeurs aléatoires et l’imbrication HTML invalide sont des coupables courants
  • Utilisez useEffect pour la logique côté client, les imports dynamiques pour les composants client uniquement, ou assurez un rendu déterministe
  • Maintenez des rendus initiaux identiques entre le serveur et le client

Qu’est-ce que l’hydratation et pourquoi échoue-t-elle ?

L’hydratation est le processus par lequel React attache les fonctionnalités JavaScript au HTML rendu côté serveur. Lorsque Next.js envoie du HTML pré-rendu au navigateur, React prend le relais en comparant le HTML du serveur avec ce qu’il rendrait côté client. S’ils ne correspondent pas, vous obtenez une erreur d’hydratation.

Considérez cela comme React qui vérifie le travail du serveur. Lorsque les rendus initiaux diffèrent, React ne peut pas attacher en toute sécurité les gestionnaires d’événements et la gestion d’état, déclenchant ainsi le besoin d’une correction d’hydratation.

Causes courantes des erreurs d’hydratation Next.js

Les API spécifiques au navigateur qui cassent le SSR

Le coupable le plus fréquent dans les problèmes de rendu côté serveur React est l’utilisation d’API du navigateur pendant le rendu initial :

// ❌ Cela casse - window n'existe pas sur le serveur
function BadComponent() {
  const width = window.innerWidth;
  return <div>Screen width: {width}px</div>;
}

Valeurs non déterministes

Les valeurs aléatoires ou les horodatages créent des sorties différentes entre le serveur et le client :

// ❌ Le serveur et le client généreront des ID différents
function RandomComponent() {
  return <div id={Math.random()}>Content</div>;
}

Différences de rendu conditionnel

Lorsque votre logique produit des structures HTML différentes :

// ❌ L'état mounted diffère entre le serveur (false) et le client (true après l'effet)
function ConditionalComponent() {
  const [mounted, setMounted] = useState(false);
  useEffect(() => setMounted(true), []);
  return <div>{mounted ? 'Client' : 'Server'}</div>;
}

Imbrication HTML invalide

Structure HTML incorrecte que les navigateurs corrigent automatiquement :

<!-- ❌ Imbrication invalide -->
<p>
  <div>This breaks hydration</div>
</p>

Trois solutions fiables pour les erreurs d’hydratation

Solution 1 : Envelopper la logique client uniquement avec useEffect

Le hook useEffect s’exécute après la fin de l’hydratation, le rendant sûr pour le code spécifique au navigateur :

function SafeComponent() {
  const [screenWidth, setScreenWidth] = useState(0);
  
  useEffect(() => {
    // Ceci s'exécute uniquement côté client après l'hydratation
    setScreenWidth(window.innerWidth);
  }, []);
  
  // Retourner un rendu initial cohérent
  if (screenWidth === 0) return <div>Loading...</div>;
  return <div>Screen width: {screenWidth}px</div>;
}

Solution 2 : Désactiver le SSR avec les imports dynamiques

Pour les composants qui dépendent fortement des API du navigateur, désactivez complètement le rendu serveur :

import dynamic from 'next/dynamic';

const ClientOnlyComponent = dynamic(
  () => import('./BrowserComponent'),
  { 
    ssr: false,
    loading: () => <div>Loading...</div> // Éviter le décalage de mise en page
  }
);

export default function Page() {
  return <ClientOnlyComponent />;
}

Solution 3 : Assurer un rendu déterministe

Générez des valeurs stables qui restent cohérentes entre les rendus :

// ✅ Utiliser des ID stables depuis les props ou les générer une seule fois
function StableComponent({ userId }) {
  // Utiliser un ID déterministe basé sur les props
  const componentId = `user-${userId}`;
  
  return <div id={componentId}>Consistent content</div>;
}

// Pour des valeurs vraiment aléatoires, générer côté serveur
export async function getServerSideProps() {
  return {
    props: {
      sessionId: generateStableId() // Générer une fois sur le serveur
    }
  };
}

Déboguer les problèmes SSR de Next.js

Pour résoudre les problèmes de débogage SSR Next.js, utilisez ces techniques :

  1. Activer les avertissements d’hydratation stricts de React en développement
  2. Comparer le HTML serveur et client en utilisant les DevTools du navigateur
  3. Ajouter des console.log pour identifier quel composant cause l’incompatibilité
  4. Utiliser React DevTools pour inspecter l’arborescence des composants
// Assistant de débogage temporaire
useEffect(() => {
  console.log('Component hydrated:', typeof window !== 'undefined');
}, []);

Bonnes pratiques pour prévenir les futures erreurs d’hydratation

Maintenir des rendus initiaux identiques : Le serveur et le client doivent produire le même HTML au premier rendu. Réservez les mises à jour dynamiques pour après l’hydratation.

Valider la structure HTML : Utilisez une imbrication appropriée et des éléments HTML valides. Des outils comme le validateur W3C peuvent détecter les problèmes tôt.

Tester avec JavaScript désactivé : Votre contenu rendu côté serveur doit être fonctionnel sans JavaScript, assurant une base solide.

Utiliser TypeScript : La vérification de type aide à détecter les incompatibilités potentielles pendant le développement plutôt qu’à l’exécution.

Conclusion

Les erreurs d’incompatibilité d’hydratation React dans Next.js sont frustrantes mais prévisibles une fois que vous comprenez le schéma. La clé est de s’assurer que votre serveur et votre client produisent un HTML initial identique. Que vous utilisiez useEffect pour la logique côté client, désactiviez le SSR avec des imports dynamiques, ou assuriez un rendu déterministe, la solution revient toujours à ce principe : maintenez le premier rendu cohérent, puis ajoutez les fonctionnalités côté client par la suite.

Rappelez-vous : si votre code dépend de l’environnement du navigateur, gardez-le en dehors du chemin de rendu initial. Vos applications Next.js s’hydrateront en douceur, et vos utilisateurs ne sauront jamais la complexité qui se déroule en coulisses.

FAQ

Oui, mais ce n'est pas recommandé. Les avertissements d'hydratation indiquent de vrais problèmes qui peuvent causer des incohérences d'interface utilisateur. Au lieu de les supprimer, corrigez la cause racine en utilisant useEffect ou des imports dynamiques pour assurer un bon fonctionnement.

Les dates s'affichent souvent différemment sur le serveur et le client en raison des différences de fuseau horaire. Utilisez un format cohérent en convertissant les dates en chaînes sur le serveur ou utilisez une bibliothèque comme date-fns avec des fuseaux horaires fixes pour les deux environnements.

Chargez les scripts tiers après l'hydratation en utilisant le composant Script de Next.js avec la stratégie afterInteractive ou lazyOnload. Pour les composants qui dépendent de ces scripts, enveloppez-les dans des imports dynamiques avec ssr false.

Désactiver le SSR signifie que ces composants n'apparaîtront pas dans le HTML initial, affectant potentiellement le SEO et augmentant le temps jusqu'à l'interactivité. Utilisez-le avec parcimonie pour les fonctionnalités vraiment dépendantes du client et fournissez des états de chargement pour éviter les décalages de mise en page.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay