Mesrai
Back to blog
// essayTechnical Deep Dive

Hydration Mismatch from Date.now() in Server Components

Real PR catch: `Date.now()` in a server component — server/client times differ, hydration warning. Client-only render fix.

Mesrai TeamAugust 6, 20268 min read

A server component rendering a timestamp via `Date.now()`. Client hydrates with its own `Date.now()`. Times differ. React logs hydration mismatch, sometimes paints stale content.

The vulnerable diff

tsxapp/page.tsx
// app/page.tsx (Next.js server component)
export default function Page() {
  // BUG: Date.now() differs between server render and client hydration
  return (
    <div>
      <TimeStamp time={Date.now()} />
    </div>
  );
}

What is wrong

Server components render once on the server, then the HTML is sent to the client where React re-runs the same render and reconciles. Anything non-deterministic — current time, random number, user-locale formatting that differs between server and client — produces different output on the two runs. React detects the mismatch, logs a warning, and in some cases throws away the server-rendered DOM and re-renders from scratch (a layout shift the user can see). The fix is to render dynamic values only on the client (`useEffect` after mount), or to pass a stable server-rendered value and live with it.

The attack

Reproducer:

textexploit
# Browser console after navigation:
Warning: Text content did not match. Server: "1759872134821" Client: "1759872135244"

# Visible: timestamp briefly shows server time, then jumps to client time.

Worse forms — locale-dependent currency formatting differing between server and client — produce visible content jumps every page load.

Mesrai's review comment

textreview-comment.txt
mesraipilot · Bot · reviewed 1 min ago

[mesrai] [code-review] [Language] [Hydration] [medium]

`Date.now()` inside a server component (or server-rendered prop)
produces a hydration mismatch. The server timestamp and client
timestamp differ by however long the HTTP roundtrip + parse took.

Two correct shapes. Pick by whether the value must be 'current' on
client or 'as-of-render' from server:

  // Option A: client-only render (no SSR for this piece)
  "use client";
  export function TimeStamp() {
    const [t, setT] = useState<number | null>(null);
    useEffect(() => setT(Date.now()), []);
    if (t === null) return null;  // server renders nothing
    return <span>{new Date(t).toLocaleString()}</span>;
  }

  // Option B: pass server time, accept the freeze
  // Server: const t = Date.now(); return <TimeStamp time={t} />
  // Client: <span>{new Date(props.time).toLocaleString()}</span>
  //   (formatting needs to match server's locale settings)

For locale-dependent rendering, pin the locale on the server and
pass it to the client component to ensure parity.

The fix

tsxcomponents/TimeStamp.tsx (fixed)
// app/page.tsx — fixed (option A)
"use client";
import { useState, useEffect } from "react";

function TimeStamp() {
  const [t, setT] = useState<number | null>(null);
  useEffect(() => setT(Date.now()), []);
  if (t === null) return null;
  return <span>{new Date(t).toLocaleString()}</span>;
}

Render nothing on server (initial state null). After mount, set state and render. No hydration mismatch because the server output and the pre-mount client output match (both empty). The timestamp appears after hydration is complete.

Why human review missed it

Hydration mismatches are a 2024-2026 Next.js-specific bug class that didn't exist in pure-client React. The bug looks like working code in dev — the warning is in the console, easy to miss. Mesrai catches `Date.now()`, `Math.random()`, and locale-dependent functions inside server-rendered trees.

Related rules + further reading

Mesrai rule pack: language/no-nondeterministic-ssr — flags non-deterministic calls in server components.

Next.js docs: Avoiding Hydration Mismatches.

React 18+ docs: useId for stable ids across SSR/CSR.

Takeaway

Server output must be deterministic. Render dynamic values only on the client. Mesrai catches every Date.now / Math.random inside SSR.

// try

See it on your next PR.

Free for individuals. Install in two minutes. Mesrai reviews every commit.