{
  "id": "ai-creates-hydration-mismatches",
  "type": "failures",
  "category": "failures",
  "locale": "pt",
  "url": "/pt/failures/ai-creates-hydration-mismatches",
  "title": "Como Corrigir Incompatibilidades de Hidratação React Causadas por IA",
  "description": "Agentes de IA geram componentes React que renderizam HTML diferente no servidor e no cliente, causando erros de hidratação e UI quebrada no primeiro carregamento.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Next.js",
    "Astro",
    "TypeScript"
  ],
  "tags": [
    "hydration",
    "react",
    "nextjs"
  ],
  "difficulty": null,
  "updated": "2026-06-08",
  "markdown": "O agente escreve componentes que produzem HTML diferente no servidor em comparação com o\ncliente — datas, valores aleatórios, verificações de `window` — fazendo com que o React lance um\nerro de incompatibilidade de hidratação e re-renderize toda a árvore no carregamento.\n\n## O sintoma\n\nUm componente lê `Date.now()`, `Math.random()` ou `window` durante a renderização,\nproduzindo saída diferente no lado do servidor vs. lado do cliente.\n\n```tsx\n// WRONG — hydration mismatch\nexport function Timestamp() {\n  return <p>Page loaded at: {new Date().toLocaleTimeString()}</p>;\n  // Server renders \"10:00:00 AM\", client renders \"10:00:01 AM\" — mismatch\n}\n\n// WRONG — conditional on window\nexport function ThemeIcon() {\n  const isDark = typeof window !== \"undefined\" && window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n  return <span>{isDark ? \"🌙\" : \"☀️\"}</span>;\n  // Server: always renders \"☀️\"; client: may render \"🌙\" — mismatch\n}\n```\n\nO console do navegador mostra:\n\n```\nError: Hydration failed because the initial UI does not match what was rendered\non the server.\n```\n\n## Por que isso acontece\n\nO agente escreve componentes que parecem corretos isoladamente. Ele não simula\ndois passes de renderização separados (SSR + hidratação do cliente) e não sabe que qualquer\nvalor que difira entre os dois quebrará a hidratação.\n\n## Como identificar\n\n- `Date.now()`, `new Date()`, `Math.random()` chamados diretamente no JSX ou durante\n  a renderização sem estarem dentro de um inicializador de `useEffect` ou `useState`.\n- Guardas `typeof window !== \"undefined\"` dentro do retorno da renderização — o servidor\n  sempre pega o ramo `false`.\n- `localStorage`, `sessionStorage` ou `navigator` lidos durante a renderização.\n- Tags HTML ou valores de atributos incompatíveis entre a saída do servidor e do cliente\n  (visível no aviso de hidratação do React DevTools).\n\n## Como corrigir\n\nAdie valores exclusivos do navegador para após a montagem, ou gere valores estáveis no lado do servidor\ne passe-os como propriedades.\n\n```tsx\n// CORRECT — defer client-only value to after mount\n\"use client\";\nimport { useState, useEffect } from \"react\";\n\nexport function Timestamp() {\n  const [time, setTime] = useState<string | null>(null);\n\n  useEffect(() => {\n    setTime(new Date().toLocaleTimeString());\n  }, []);\n\n  if (!time) return <p>Loading time…</p>; // matches server output\n  return <p>Page loaded at: {time}</p>;\n}\n```\n\n```tsx\n// CORRECT — theme icon: use CSS / data attribute, avoid JS render branch\n// Set data-theme on <html> in a blocking inline script (not React)\n// Then use CSS: [data-theme=\"dark\"] .icon { content: \"🌙\" }\n\n// Or use Next.js next-themes which handles SSR safely:\nimport { useTheme } from \"next-themes\";\nimport { useEffect, useState } from \"react\";\n\nexport function ThemeIcon() {\n  const { resolvedTheme } = useTheme();\n  const [mounted, setMounted] = useState(false);\n  useEffect(() => setMounted(true), []);\n  if (!mounted) return <span style={{ width: 24 }} />; // stable placeholder\n  return <span>{resolvedTheme === \"dark\" ? \"🌙\" : \"☀️\"}</span>;\n}\n```\n\n```txt\n[ ] No Date.now() / Math.random() / new Date() called directly during render\n[ ] No typeof window / localStorage / navigator accessed during render\n[ ] Browser-only state initializes to null/undefined, set in useEffect\n[ ] Render a stable placeholder (same as server output) until after mount\n[ ] Use suppressHydrationWarning only on elements where the mismatch is intentional (e.g. timestamp)\n[ ] Run \"next build && next start\" and check browser console for hydration errors\n```\n\n## Prompt de Correção\n\n```txt title=\"Fix Prompt\"\nThis component causes a React hydration mismatch because it reads browser-only\nvalues (Date, window, localStorage, Math.random) during render. Fix it: move\nall browser-only reads into a useEffect that sets state after mount, render a\nstable placeholder on first render that matches what the server produces, and\nnever conditionally return different JSX trees based on typeof window. Explain\nwhy each change prevents the mismatch.\n```\n\n## Teste\n\n```bash\n# Build and start, then check for hydration errors with curl diff\nnext build 2>&1 | grep -i \"hydrat\\|mismatch\" && echo \"FAIL: hydration error in build\" || echo \"Build OK\"\n\n# Runtime: open browser console after next start and look for hydration warnings\nnext start &\nsleep 3\ncurl -s http://localhost:3000 | grep -i \"data-nextjs-scroll-focus-boundary\" > /dev/null && echo \"Server rendered OK\"\n```"
}