The eslint-plugin-react-hooks purity rule catches impure calls such as
Date.now() and Math.random() during render. It helps keep components stable
across renders. eslint-plugin-hydration-proof asks a different question: will
the server and browser render the same HTML? Its 15 rules also cover browser
storage, locale, time zones and invalid markup. You can use both plugins.
At a glance
| The purity rule | eslint-plugin-hydration-proof | |
|---|---|---|
| Goal | Components and hooks are pure, so re-renders and memoization are safe | The server render and the hydration render produce the same output |
| Comes with | The recommended preset of eslint-plugin-react-hooks 7, and eslint-config-next | Its own recommended, next and strict presets |
| Clock and random values | Date.now(), Math.random(), performance.now() in 7.1.1 | Those, plus new Date(), crypto.randomUUID(), uuid, nanoid, lodash and more |
Lazy useState initializers | Accepted | Reported: the initializer runs on the server and again during hydration |
| Locale and timezone formatting | Not checked | toLocaleString(), Intl.* and date getters without an explicit locale or timezone |
Browser APIs, storage, matchMedia | Not checked | Reported in render and initial state |
| Invalid HTML nesting | Not checked | <div> in <p>, <a> in <a>, table structure |
suppressHydrationWarning | Not checked | Audited |
| Server Components | No special handling | Skipped by the next preset, where the clock cannot cause a mismatch |
| Message | Why the call is impure | What the server and the browser render, and the fix |
What the eslint-plugin-react-hooks purity rule does well
react-hooks/purity is one of the React Compiler's rules, now shipped in eslint-plugin-react-hooks. React's docs say it "validates that components/hooks are pure by checking that they do not call known-impure functions" (purity). It reports:
Error: Cannot call impure function during render
`Date.now` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render.- It is on by default.
purityis in therecommendedpreset of eslint-plugin-react-hooks 7, andeslint-config-nextincludes that preset, so Next.js apps that use it already run the rule. - It sees data flow. It runs the compiler's analysis, not a pattern match.
- It covers more than hydration. Impure renders break memoization and the React Compiler, not only server rendering. Its docs name hydration mismatches as one of the bugs impurity causes.
The React Compiler's lint rules used to live in a separate package, eslint-plugin-react-compiler. React's Compiler 1.0 announcement says to remove it and use eslint-plugin-react-hooks@latest instead.
Where hydration-proof differs
Pure is not the same as hydration-safe. A value can be stable across re-renders and still differ between the server and browser. For example, a lazy state initializer runs once in each environment:
import { useState } from "react";
function Item() {
// Stable across re-renders, so react-hooks/purity accepts it.
// The server and the browser each run the initializer once, and get
// different UUIDs: the hydration render does not match the server HTML.
const [id] = useState(() => crypto.randomUUID());
return <div id={id}>Content</div>;
}eslint-plugin-hydration-proof reports this as
no-unstable-id. The same problem occurs with
useState(() => Date.now()) when the value appears in the HTML: each side
starts with a different time.
More causes than the clock. Many hydration mismatches never call an impure function:
no-locale-without-explicit-localeandno-timezone-without-explicit-timezone: formatting that depends on the machine;no-browser-global-in-render,no-storage-in-initial-render,no-match-media-in-renderandno-window-render-branch: code that sees the browser;no-invalid-interactive-nesting: HTML the browser repairs;require-stable-server-snapshotandaudit-suppress-hydration-warning.
new Date(). In eslint-plugin-react-hooks 7.1.1, the compiler marks Date.now, Math.random and performance.now as impure. A reported gap was that new Date().getTime() and new Date().getFullYear() were not flagged; the fix, which treats new Date() without arguments as impure, was merged on September 15, 2026 and is not in a stable release yet. no-date-in-render reports new Date(), Date() and Temporal.Now today.
Server Components. With the next preset, eslint-plugin-hydration-proof skips Server Components, where reading the clock cannot cause a mismatch because the browser receives the result, not the code.
Use both
Keep the purity rule for what it protects, and add eslint-plugin-hydration-proof for hydration:
import { defineConfig } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import hydrationProof from "eslint-plugin-hydration-proof";
export default defineConfig([...nextVitals, hydrationProof.configs.next]);A Date.now() or Math.random() in render is then reported twice, once by each plugin, with different reasons. Keep both reports, or turn one of the overlapping rules off. The ESLint plugin guide covers presets and settings.
npm install -D eslint-plugin-hydration-proofA linter reads one file at a time. Values passed through props or context, nesting across components, CSS-in-JS and anything that happens outside your code need hydration-proof test, which checks the running app.
Sources
As of September 2026:
- react.dev: purity and eslint-plugin-react-hooks, the rule and the
recommendedpreset - eslint-plugin-react-hooks 7.1.1 on npm, the message text and the impure functions in its source
- facebook/react#37553 and facebook/react#37576,
new Date()in the purity rule - Next.js: ESLint plugin,
eslint-config-nextincludes therecommendedrules of eslint-plugin-react-hooks - React Compiler 1.0,
eslint-plugin-react-compilerreplaced by eslint-plugin-react-hooks