A Next.js date hydration error happens when a component reads the current time during render. The server renders first; the browser renders later, so the values can differ. Pass the server's time as a prop, or read the clock after hydration.
Symptoms
React uses different wording depending on its version and whether you run a development or production build:
Hydration failed because the server rendered text didn't match the client.
Text content does not match server-rendered HTML.
Warning: Text content did not match. Server: "12:04:31" Client: "12:04:33"
Minified React error #418; visit https://react.dev/errors/418hydration-proof reports a text difference as HP1001 or an attribute difference as HP1002. The likely cause is shown as Time-dependent value. Use the two values in the finding to check whether the clock is involved.
If it happens only sometimes, look for rounded values such as "3 minutes ago". The text changes only when the two renders fall on opposite sides of the rounding boundary.
Why it happens
These calls read the clock during render:
Date.now(),new Date()andDate()with no argumentsperformance.now()andTemporal.Now.*()- relative formatting built on them: "3 minutes ago", countdowns, "today"
React compares the first browser render with the server HTML. A new Date()
hydration mismatch or a Date.now() hydration mismatch occurs when the clock
has moved enough to change the displayed value. A value rounded to a minute
may match for a while; a millisecond timestamp will not.
How to fix it
Pass the server's time as a prop
Read the clock on the server and pass that value to the client. Both renders then start from the same number:
// A Server Component: it only renders on the server.
export default async function Page() {
const post = await getPost();
return <PostDate publishedAt={post.publishedAt} renderedAt={Date.now()} />;
}"use client";
export function PostDate({
publishedAt,
renderedAt,
}: {
publishedAt: number;
renderedAt: number;
}) {
const minutes = Math.round((renderedAt - publishedAt) / 60_000);
return <p>Updated {minutes} minutes ago</p>;
}Render the time after hydration
For a live clock or countdown, render a placeholder first. Fill it in from an effect after hydration, so the first browser render still matches the server:
"use client";
import { useEffect, useState } from "react";
export function Countdown({ endsAt }: { endsAt: number }) {
const [now, setNow] = useState<number | null>(null);
useEffect(() => {
setNow(Date.now());
const id = setInterval(() => setNow(Date.now()), 1000);
return () => clearInterval(id);
}, []);
if (now === null) return <span>–</span>;
return <span>{Math.round((endsAt - now) / 1000)}s left</span>;
}useEffect and two-pass rendering explains the pattern and when the flash of the placeholder matters.
Format a fixed date, not "now"
A stored date such as new Date(post.publishedAt) has the same underlying
value on both sides. Formatting can still differ if the server and browser
use different timezones or
locales. Set those explicitly too.
How to solve hydration errors related to dates in React without hiding them
suppressHydrationWarning stops React from reporting the difference, but it
does not update the text. Use it only when the first value does not matter,
such as a live clock that updates immediately. Otherwise, visitors may see
stale text until the next render. See
when suppressHydrationWarning is safe.
Catch it with ESLint
no-date-in-render reports Date.now(),
new Date(), performance.now() and Temporal.Now in render code, and skips
Server Components with the next preset:
npm install -D eslint-plugin-hydration-proofno-timezone-without-explicit-timezone
catches the formatting half.
Catch it in CI
hydration-proof test checks each route for text and attribute differences.
Add --probe to reload a page with the browser clock changed. If its client
value moves with the clock, the cause is proven. See probes.
npx hydration-proof test --probe