Cómo solucionar los errores de hidratación en React causados por IA
Los agentes de IA generan componentes de React que renderizan HTML diferente en el servidor y el cliente, lo que provoca errores de hidratación y una interfaz rota en la primera carga.
El agente escribe componentes que producen HTML diferente en el servidor y el
cliente — fechas, valores aleatorios, comprobaciones de window — lo que hace que React lance un
error de hidratación y vuelva a renderizar todo el árbol al cargar.
El síntoma
Un componente lee Date.now(), Math.random() o window durante el renderizado,
produciendo una salida diferente en el servidor y en el cliente.
// WRONG — hydration mismatchexport function Timestamp() { return <p>Page loaded at: {new Date().toLocaleTimeString()}</p>; // Server renders "10:00:00 AM", client renders "10:00:01 AM" — mismatch}
// WRONG — conditional on windowexport function ThemeIcon() { const isDark = typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches; return <span>{isDark ? "🌙" : "☀️"}</span>; // Server: always renders "☀️"; client: may render "🌙" — mismatch}La consola del navegador muestra:
Error: Hydration failed because the initial UI does not match what was renderedon the server.Por qué ocurre
El agente escribe componentes que parecen correctos de forma aislada. No simula dos pases de renderizado separados (SSR + hidratación del cliente) y no sabe que cualquier valor que difiera entre ambos romperá la hidratación.
Cómo detectarlo
Date.now(),new Date(),Math.random()llamados directamente en JSX o durante el renderizado sin estar dentro de un inicializador deuseEffectouseState.- Guardias
typeof window !== "undefined"dentro del retorno del renderizado — el servidor siempre toma la ramafalse. - Lectura de
localStorage,sessionStorageonavigatordurante el renderizado. - Etiquetas HTML o valores de atributos diferentes entre la salida del servidor y el cliente (visible en la advertencia de hidratación de React DevTools).
Cómo solucionarlo
Diferir los valores solo del navegador hasta después del montaje, o generar valores estables en el servidor y pasarlos como props.
// CORRECT — defer client-only value to after mount"use client";import { useState, useEffect } from "react";
export function Timestamp() { const [time, setTime] = useState<string | null>(null);
useEffect(() => { setTime(new Date().toLocaleTimeString()); }, []);
if (!time) return <p>Loading time…</p>; // matches server output return <p>Page loaded at: {time}</p>;}// CORRECT — theme icon: use CSS / data attribute, avoid JS render branch// Set data-theme on <html> in a blocking inline script (not React)// Then use CSS: [data-theme="dark"] .icon { content: "🌙" }
// Or use Next.js next-themes which handles SSR safely:import { useTheme } from "next-themes";import { useEffect, useState } from "react";
export function ThemeIcon() { const { resolvedTheme } = useTheme(); const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); if (!mounted) return <span style={{ width: 24 }} />; // stable placeholder return <span>{resolvedTheme === "dark" ? "🌙" : "☀️"}</span>;}[ ] No Date.now() / Math.random() / new Date() called directly during render[ ] No typeof window / localStorage / navigator accessed during render[ ] Browser-only state initializes to null/undefined, set in useEffect[ ] Render a stable placeholder (same as server output) until after mount[ ] Use suppressHydrationWarning only on elements where the mismatch is intentional (e.g. timestamp)[ ] Run "next build && next start" and check browser console for hydration errorsPrompt de corrección
This component causes a React hydration mismatch because it reads browser-onlyvalues (Date, window, localStorage, Math.random) during render. Fix it: moveall browser-only reads into a useEffect that sets state after mount, render astable placeholder on first render that matches what the server produces, andnever conditionally return different JSX trees based on typeof window. Explainwhy each change prevents the mismatch.Prueba
# Build and start, then check for hydration errors with curl diffnext build 2>&1 | grep -i "hydrat\|mismatch" && echo "FAIL: hydration error in build" || echo "Build OK"
# Runtime: open browser console after next start and look for hydration warningsnext start &sleep 3curl -s http://localhost:3000 | grep -i "data-nextjs-scroll-focus-boundary" > /dev/null && echo "Server rendered OK"