Remix hydration mismatch (server and client HTML differ) in CI
Remix renders HTML on the server and React hydrates it on the client. If the two renders differ, React warns about a hydration mismatch and may discard the server HTML. The cause is usually non-deterministic output like Date.now(), Math.random(), or values that differ between server and client.
What this error means
The browser console shows "Hydration failed because the initial UI does not match what was rendered on the server", and an end-to-end test in CI fails on the mismatched markup.
Warning: Text content did not match. Server: "12:04:31" Client: "12:04:32"
Error: Hydration failed because the initial UI does not match what was
rendered on the server.Common causes
Non-deterministic values in render
Rendering new Date(), Math.random(), or locale-dependent formatting produces different HTML on server and client.
Browser-only APIs read during render
Reading window, localStorage, or document during the initial render diverges from the server render, which has none of these.
How to fix it
Move volatile values out of the first render
- Compute time/random values on the server in the loader and pass them down, or defer them to
useEffect. - Guard browser-only reads behind
useEffectso they run after hydration. - Ensure locale and timezone formatting is deterministic.
useEffect(() => {
setNow(new Date()); // client-only, after hydration
}, []);Suppress unavoidable text mismatches narrowly
For a single unavoidable dynamic node, use suppressHydrationWarning on that element only, not app-wide.
How to prevent it
- Compute dates and random values in the loader, not in render.
- Read
window/document/localStorageonly insideuseEffect. - Keep locale and timezone formatting deterministic across environments.