# no-random-in-render

> Math.random() in render, crypto.randomUUID(), uuid and nanoid return a new value on each render, so hydration fails. This rule reports them in React code.

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

Math.random() in render returns one value on the server and another when the
browser hydrates, so the HTML and the first client render disagree.
`no-random-in-render` reports `Math.random()`, `crypto.randomUUID()`, uuid,
nanoid and lodash's random helpers in render code, and points you to a value
the server creates or `useId()`.

| | |
| --- | --- |
| Rule | `hydration-proof/no-random-in-render` |
| What it reports | Disallow random values 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):

- `Math.random()`
- `crypto.randomUUID()` and `crypto.getRandomValues()` (the global,
  `window.crypto`, or `node:crypto`'s `randomUUID`, `randomBytes`,
  `randomInt`)
- `v1`, `v4`, `v6` and `v7` imported from `uuid` (named or namespace imports;
  `v3` and `v5` are deterministic and allowed)
- `nanoid()` from `nanoid` (and `nanoid/non-secure`), and functions created at
  module level with `customAlphabet()` or `customRandom()`
- `uniqueId`, `random` and `sample` from `lodash`, `lodash-es`,
  `lodash/<name>` or `lodash.<name>`

Only imports are matched, so a local function called `nanoid` is not
reported.

## Why Math.random() in render breaks hydration

Every render produces a new value, and the hydration render is a new render:

```text
server HTML:   <div class="card card-0.7281">
client render: <div class="card card-0.1942">
```

Text differences are reported by React and make it render the page again.
Attribute differences (`className`, `style`, `data-*`) are worse: React 19
keeps the server value **without reporting it** in production, so the page
silently runs with the wrong attribute. See
[attributes didn't match](https://hydration.jscrate.dev/docs/errors/tree-hydrated-but-attributes-didnt-match)
for that case.

## Incorrect

```jsx
import { v4 as uuid } from "uuid";
import { sample } from "lodash";

function Tip({ tips }) {
  return <p>{sample(tips)}</p>;
}

function Card() {
  const [seed] = useState(() => Math.random());
  return <div data-seed={seed} />;
}

function Upload() {
  const key = uuid();
  return <Dropzone key={key} />;
}
```

## Correct

Pick on the server and pass the result down. With the `next` preset, this
file is a Server Component and is not checked:

```jsx title="app/tips/page.jsx" filename="app/tips/page.jsx" preset="next"
export default async function Page() {
  const tips = await getTips();
  return <Tip tip={tips[Math.floor(Math.random() * tips.length)]} />;
}
```

Or render something stable first and randomize after hydration:

```jsx
function Tip({ tips }) {
  const [tip, setTip] = useState(tips[0]);
  useEffect(
    () => setTip(tips[Math.floor(Math.random() * tips.length)]),
    [tips]
  );
  return <p>{tip}</p>;
}

// Event handlers run after hydration.
function Upload() {
  const onDrop = (files) => save(files, crypto.randomUUID());
  return <Dropzone onDrop={onDrop} />;
}
```

For ids, use `useId()` (see [`no-unstable-id`](https://hydration.jscrate.dev/docs/rules/no-unstable-id)).

## Options

This rule has no options.

## Messages

What ESLint prints for this rule, word for word:

- `<source>` returns a different value on the server and during hydration, so the rendered output does not match. Create the value on the server and pass it down, use useId() for ids, or generate it in useEffect after hydration.

## When not to use it

When the component is never server-rendered, or when the random value is only
used for something that never reaches the DOM and never changes what is
rendered.

## Related

- [`no-unstable-id`](https://hydration.jscrate.dev/docs/rules/no-unstable-id) reports random values that
  end up in ids. Those calls are not reported by this rule.
- [`require-deterministic-list-order`](https://hydration.jscrate.dev/docs/rules/require-deterministic-list-order)
  reports random sort comparators and lodash's `shuffle`/`sampleSize`. Those
  are not reported by this rule.
- [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 `Math.random()` during render. This rule explains the
  hydration consequence, skips Server Components, and also covers `crypto`,
  uuid, nanoid and lodash helpers. With both enabled you see two reports for
  `Math.random()`; turn one off if you prefer a single report.
- [Random values and hydration](https://hydration.jscrate.dev/docs/causes/random): every fix for this
  cause
