P PasteCode
Error

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.

CursorClaude CodeCodexWindsurf Next.jsAstroTypeScript
.md .json Actualizado 8 jun 2026

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 mismatch
export 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 window
export 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 rendered
on 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 de useEffect o useState.
  • Guardias typeof window !== "undefined" dentro del retorno del renderizado — el servidor siempre toma la rama false.
  • Lectura de localStorage, sessionStorage o navigator durante 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 errors

Prompt de corrección

Fix Prompt
This component causes a React hydration mismatch because it reads browser-only
values (Date, window, localStorage, Math.random) during render. Fix it: move
all browser-only reads into a useEffect that sets state after mount, render a
stable placeholder on first render that matches what the server produces, and
never conditionally return different JSX trees based on typeof window. Explain
why each change prevents the mismatch.

Prueba

Terminal window
# Build and start, then check for hydration errors with curl diff
next 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 warnings
next start &
sleep 3
curl -s http://localhost:3000 | grep -i "data-nextjs-scroll-focus-boundary" > /dev/null && echo "Server rendered OK"