facebook/react · error · Error
407
407
Error message
Missing getServerSnapshot, which is required for server-rendered content. Will revert to client rendering.
What it means
When a component using useSyncExternalStore mounts, React reads a snapshot. If that mount render is hydrating server-rendered HTML (getIsHydrating() true) and no third getServerSnapshot argument was supplied, React cannot reproduce the value the server rendered from, so it throws instead of silently mismatching hydration.
Solutions
- Pass a third getServerSnapshot argument returning the stable server-side value, e.g. useSyncExternalStore(subscribe, () => window.innerWidth, () => 0)
- Cache the server snapshot (repeat calls must return the same value, or you get the separate uncached-getServerSnapshot warning)
- For truly client-only UI, gate rendering behind a mounted flag (useEffect sets state) so hydration never sees the store value
- If the store can report its initial value synchronously, reuse getSnapshot as getServerSnapshot only when it is safe to call on the server
Example fix
// before const scrollY = useSyncExternalStore( subscribeScroll, () => window.scrollY, // throws during hydration: no server snapshot ); // after const scrollY = useSyncExternalStore( subscribeScroll, () => window.scrollY, () => 0, // stable server-rendered value );
Defensive patterns
Strategy: validation
Validate before calling
// Wrapper that refuses the 2-argument form in any SSR-capable app
export function useSyncExternalStoreSSR<T>(
subscribe: () => () => void,
getSnapshot: () => T,
getServerSnapshot: () => T,
): T {
if (typeof getServerSnapshot !== 'function') {
throw new Error('getServerSnapshot is required when hydration is possible');
}
return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
} Type guard
// Type that makes the server snapshot non-optional at every call site type SSRStoreHook = <T>( subscribe: () => () => void, getSnapshot: () => T, getServerSnapshot: () => T, // required, not `?:` ) => T;
Prevention
- Always pass the third getServerSnapshot argument when a component can be server-rendered
- Return a cached/stable value from getServerSnapshot (constants or memoized) to also avoid the uncached-snapshot warning
- For browser-only values, render a constant on the server and update after mount via a mounted flag
- Ban the two-argument form in code review for SSR apps
When it happens
Trigger: useSyncExternalStore(subscribe, getSnapshot) called with only two arguments during hydration — i.e. the first client render over SSR HTML (Next.js, Remix, react-dom/hydrateRoot).
Common situations: Adding a browser-only store subscription (window size, matchMedia, localStorage, third-party stores) to a component that is server-rendered; hydration failures after introducing the hook in an SSR framework; libraries integrating external stores without a server snapshot.
Related errors
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/d590ba7af02c1d1b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/react-reconciler/src/ReactFiberHooks.js:1660
queue.lastRenderedState = newState;
}
return [newState, dispatch];
}
function mountSyncExternalStore<T>(
subscribe: (() => void) => () => void,
getSnapshot: () => T,
getServerSnapshot?: () => T,
): T {
const fiber = currentlyRenderingFiber;
const hook = mountWorkInProgressHook();
let nextSnapshot;
const isHydrating = getIsHydrating();
if (isHydrating) {
if (getServerSnapshot === undefined) {
throw new Error(
'Missing getServerSnapshot, which is required for ' +
'server-rendered content. Will revert to client rendering.',
);
}
nextSnapshot = getServerSnapshot();
if (__DEV__) {
if (!didWarnUncachedGetSnapshot) {
if (nextSnapshot !== getServerSnapshot()) {
console.error(
'The result of getServerSnapshot should be cached to avoid an infinite loop',
);
didWarnUncachedGetSnapshot = true;
}
}
}
} else {
nextSnapshot = getSnapshot();
if (__DEV__) {View on GitHub (pinned to eafeac097b)