# require-stable-server-snapshot

> useSyncExternalStore needs a getServerSnapshot that returns the same value on the server and during hydration. This rule reports a missing or unstable one.

Source: https://hydration.jscrate.dev/docs/rules/require-stable-server-snapshot
Last updated: 2026-09-18

`useSyncExternalStore` needs a getServerSnapshot, its third argument, to
render on the server and to hydrate, and that function must return the same
value in both places. `require-stable-server-snapshot` reports a missing
`getServerSnapshot`, and one that reads the browser, the clock or random
values.

| | |
| --- | --- |
| Rule | `hydration-proof/require-stable-server-snapshot` |
| What it reports | Require useSyncExternalStore to have a getServerSnapshot that returns the same value on the server and during hydration |
| recommended / next | Error |
| strict | Error |
| Server Components | Skipped with the next preset |
| Suggestions | No |
| Options | none |

## What it reports

- `useSyncExternalStore(subscribe, getSnapshot)` without a third argument (or
  with `undefined`), including `React.useSyncExternalStore` and the
  `use-sync-external-store/shim` export.
- Inside `getServerSnapshot` (an inline function, or a function declared in
  the same file and passed by name): any read of a browser-only global,
  `localStorage`/`sessionStorage`, `matchMedia`, the clock (`Date.now()`,
  `new Date()`, ...), random values (`Math.random()`, ...) and environment
  checks (`typeof window`).

Functions nested inside `getServerSnapshot` and functions imported from other
files are not inspected.

## Why getServerSnapshot must be stable

During server rendering React calls `getServerSnapshot`; without it,
rendering the component on the server fails and React renders it again in the
browser ([Missing getServerSnapshot](https://hydration.jscrate.dev/docs/errors/missing-getserversnapshot)
is that error). During hydration React calls `getServerSnapshot` again, in the
browser, and expects the value the server used. Only after hydration does it
switch to `getSnapshot`:

```text
server:     getServerSnapshot() → typeof window === 'undefined' → 'light'
hydration:  getServerSnapshot() → typeof window === 'undefined' → 'dark'   (mismatch)
```

So `getServerSnapshot` must be a pure function of data both sides have.

## Incorrect

```jsx
function useOnline() {
  return useSyncExternalStore(subscribe, () => navigator.onLine);
}

function useTheme() {
  return useSyncExternalStore(
    subscribeToStorage,
    () => localStorage.getItem("theme"),
    () => localStorage.getItem("theme") ?? "light"
  );
}

const getWidth = () => window.innerWidth;

function useWidth() {
  return useSyncExternalStore(subscribeToResize, getWidth, getWidth);
}
```

## Correct

```jsx
function useOnline() {
  return useSyncExternalStore(
    subscribe,
    () => navigator.onLine,
    () => true
  );
}

function useTheme(serverTheme) {
  return useSyncExternalStore(
    subscribeToStorage,
    () => localStorage.getItem("theme") ?? serverTheme,
    () => serverTheme // read from a cookie on the server and passed as a prop
  );
}

const getWidth = () => window.innerWidth;
const getServerWidth = () => 1024;

function useWidth() {
  return useSyncExternalStore(subscribeToResize, getWidth, getServerWidth);
}
```

## Options

This rule has no options.

## Messages

What ESLint prints for this rule, word for word:

- `useSyncExternalStore` has no getServerSnapshot. React needs it to render on the server and to hydrate: without it the server render errors and the component is rendered again in the browser. Pass a third argument that returns the value the server renders.
- getServerSnapshot reads `<read>`. It must return the same value on the server and while hydrating, so it cannot depend on the browser, the clock or randomness. Return a constant or data passed from the server.

## When not to use it

When the store is only used in components that are never server-rendered.

## Related

- [`no-client-only-initial-state`](https://hydration.jscrate.dev/docs/rules/no-client-only-initial-state),
  [`no-storage-in-initial-render`](https://hydration.jscrate.dev/docs/rules/no-storage-in-initial-render)
  and [`no-match-media-in-render`](https://hydration.jscrate.dev/docs/rules/no-match-media-in-render)
  recommend `useSyncExternalStore` with a server snapshot as a fix.
- [Missing getServerSnapshot](https://hydration.jscrate.dev/docs/errors/missing-getserversnapshot): the
  React error, decoded
- [localStorage and sessionStorage read during render](https://hydration.jscrate.dev/docs/causes/storage),
  the most common store behind `useSyncExternalStore`
