{
  "id": "ai-creates-hydration-mismatches",
  "type": "failures",
  "category": "failures",
  "locale": "de",
  "url": "/de/failures/ai-creates-hydration-mismatches",
  "title": "So beheben Sie Hydrierungsinkonsistenzen durch KI-generierte React-Komponenten",
  "description": "KI-Agenten generieren React-Komponenten, die auf Server und Client unterschiedliches HTML rendern, was zu Hydrierungsfehlern und einer defekten Benutzeroberfläche beim ersten Laden führt.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Next.js",
    "Astro",
    "TypeScript"
  ],
  "tags": [
    "hydration",
    "react",
    "nextjs"
  ],
  "difficulty": null,
  "updated": "2026-06-08",
  "markdown": "Der Agent schreibt Komponenten, die auf dem Server und auf dem Client\nunterschiedliches HTML erzeugen – Daten, Zufallswerte, `window`-Prüfungen –,\nwas dazu führt, dass React einen Hydrierungs-Mismatch-Fehler auslöst und den\ngesamten Baum beim Laden neu rendert.\n\n## Das Symptom\n\nEine Komponente liest `Date.now()`, `Math.random()` oder `window` während des\nRenderns und erzeugt dadurch auf Server- und Client-Seite unterschiedliche Ausgaben.\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\nDie Browserkonsole zeigt:\n\n```\nError: Hydration failed because the initial UI does not match what was rendered\non the server.\n```\n\n## Warum es passiert\n\nDer Agent schreibt Komponenten, die isoliert betrachtet korrekt erscheinen. Er\nsimuliert keine zwei separaten Renderdurchläufe (SSR + Client-Hydrierung) und\nweiß nicht, dass jeder Wert, der zwischen beiden abweicht, die Hydrierung\nunterbricht.\n\n## Wie man es erkennt\n\n- `Date.now()`, `new Date()`, `Math.random()` werden direkt in JSX oder während\ndes Renderns aufgerufen, ohne innerhalb eines `useEffect` oder `useState`-Initialisierers zu sein.\n- `typeof window !== \"undefined\"`-Abfragen innerhalb des Render-Rückgabewerts – der Server\nnimmt immer den `false`-Zweig.\n- `localStorage`, `sessionStorage` oder `navigator` werden während des Renderns gelesen.\n- Unterschiedliche HTML-Tags oder Attributwerte zwischen Server- und Client-Ausgabe\n(sichtbar in der React DevTools Hydrierungswarnung).\n\n## Wie man es behebt\n\nVerschieben Sie nur im Browser verfügbare Werte in die Zeit nach dem Mount, oder\nerzeugen Sie stabile Werte auf dem Server und übergeben Sie sie als Props.\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## Fix-Prompt\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## Test\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```"
}