# no-date-in-render

> Calling Date.now() in render, new Date() or performance.now() gives the server and the browser different times. This rule reports it and shows the fix.

Source: https://hydration.jscrate.dev/docs/rules/no-date-in-render
Last updated: 2026-09-18

Calling Date.now() in render makes the output depend on the moment the
component runs: the server renders at one time, the browser hydrates at
another, and the text no longer matches. `no-date-in-render` reports
`Date.now()`, `new Date()`, `performance.now()` and `Temporal.Now` in render
code, and tells you where to read the time instead.

| | |
| --- | --- |
| Rule | `hydration-proof/no-date-in-render` |
| What it reports | Disallow reading the current time while a component renders |
| recommended / next | Error |
| strict | Error |
| Server Components | Skipped with the next preset |
| Suggestions | No |
| Options | none |

## What it reports

These calls in [render code](https://hydration.jscrate.dev/docs/eslint#what-counts-as-render):

- `Date.now()`
- `new Date()` and `Date()` without arguments
- `performance.now()`
- `Temporal.Now.*()`

It also finds them through `window.`, `self.` and `globalThis.`, and ignores
local variables that happen to be called `Date` or `performance`. Dates built
from explicit values (`new Date(post.publishedAt)`) are fine.

## Why Date.now() in render breaks hydration

The server renders the component at one moment, and the browser renders it
again when it hydrates, a few hundred milliseconds (or, with a cached page,
days) later:

```text
server HTML:   <p>Rendered at 1767225600000</p>
client render: <p>Rendered at 1767225600412</p>
```

React finds different text, reports a hydration error and, in React 19, throws
the server HTML away and renders the page again on the client.
[Date and time hydration errors](https://hydration.jscrate.dev/docs/causes/time) covers every fix in
detail.

## Incorrect

```jsx
function Footer() {
  return <p>© {new Date().getFullYear()}</p>;
}

function Countdown({ endsAt }) {
  const left = endsAt - Date.now();
  return <span>{Math.round(left / 1000)}s</span>;
}

function Timer() {
  const [start] = useState(() => performance.now());
  return <Elapsed since={start} />;
}
```

## Correct

Pass the time from a Server Component, so both renders use the same value.
With the `next` preset, this file is a Server Component and is not checked:

```jsx title="app/sale/page.jsx" filename="app/sale/page.jsx" preset="next"
export default async function Page() {
  return <Countdown endsAt={deadline} now={Date.now()} />;
}
```

Or render a placeholder and read the clock after hydration:

```jsx
function Countdown({ endsAt }) {
  const [now, setNow] = useState(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</span>;
}

// Explicit dates are deterministic.
export function Published({ at }) {
  return <time dateTime={at}>{new Date(at).toISOString().slice(0, 10)}</time>;
}
```

Reading the clock in event handlers, effects and `useCallback` callbacks is
fine: they run after hydration.

## Options

This rule has no options.

## Messages

What ESLint prints for this rule, word for word:

- `<source>` reads the clock during render. The server renders one time and the browser another while hydrating, so the output does not match. Pass the time from the server (props or data), or read it in useEffect after hydration.

## When not to use it

When the component is never server-rendered (a client-only app, or a
component loaded with [`next/dynamic` and `ssr: false`](https://hydration.jscrate.dev/docs/guides/next-dynamic-ssr-false)),
or when the output is wrapped in an element with `suppressHydrationWarning` on
purpose (for example a live clock).

## Related

- [`no-unstable-id`](https://hydration.jscrate.dev/docs/rules/no-unstable-id) reports the clock when the
  value ends up in an `id` (or `htmlFor`, `aria-*`, `name`). Those calls are
  not reported by this rule.
- [`no-timezone-without-explicit-timezone`](https://hydration.jscrate.dev/docs/rules/no-timezone-without-explicit-timezone)
  reports `new Date().getHours()` for the time zone; this rule reports the
  `new Date()` in it. They are different problems.
- [`require-stable-server-snapshot`](https://hydration.jscrate.dev/docs/rules/require-stable-server-snapshot)
  reports the clock inside `getServerSnapshot`.
- [eslint-plugin-react-hooks](https://hydration.jscrate.dev/docs/compare/eslint-plugin-react-hooks): its
  `purity` rule (part of the React Compiler rules) also flags known impure
  calls such as `Date.now()` during render, because they make renders
  unpredictable. This rule explains the hydration consequence, skips Server
  Components, covers `performance.now()` and `Temporal.Now`, and hands ids to
  `no-unstable-id`. With both enabled you see two reports for the same call;
  turn one off if you prefer a single report.
- [Text content does not match server-rendered HTML](https://hydration.jscrate.dev/docs/errors/text-content-does-not-match-server-rendered-html),
  the error this rule prevents
