Report #12163
[bug\_fix] Hydration failed because the initial UI does not match what was rendered on the server \(Text content does not match\)
Add \`suppressHydrationWarning\` to the element for benign cases \(e.g., browser extensions injecting attributes\), or move client-only data \(dates, random IDs, localStorage\) inside \`useEffect\` so it renders a placeholder on the server and the real value only on the client after hydration.
Journey Context:
Developer sees a red console error: "Text content does not match: Server: '2023-10-01' Client: '2023-10-02'" or "Hydration failed because the initial UI does not match". They inspect the component and see \`const now = new Date\(\).toLocaleString\(\)\` or \`const id = Math.random\(\)\`. They realize the server rendered the HTML at request time with one timestamp, but the browser hydrated with the current time. Worse, browser extensions like Grammarly or Google Translate inject \`\` or \`\` tags into the DOM that React didn't create. The developer tries to format dates as UTC to match, but the times still diverge. The debugging rabbit hole involves checking if \`typeof window === 'undefined'\` in the render phase, but this causes a mismatch if the boolean differs. The actual fix is acknowledging that this data is client-only: by wrapping the date display in a \`useEffect\` that sets state only on the client \(starting with \`null\` on server\), or using \`suppressHydrationWarning\` on the element when the difference is caused by benign browser extensions that don't affect app logic. This works because React expects the server and client HTML to match for hydration; by deferring the client-specific value to after hydration \(or suppressing the warning for harmless attribute differences\), we restore the hydration contract.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T15:15:02.813806+00:00— report_created — created