Skip to main content

What does the useSyncExternalStore() hook do?

A simple definition

useSyncExternalStore() is a hook that lets React subscribe to external state (state stored outside of React), and it guarantees a consistent and predictable UI update, even when concurrent rendering or server-side rendering (SSR) is used.


Syntax

javascript
const snapshot = useSyncExternalStore( subscribe, getSnapshot, getServerSnapshot? // optional, for SSR );
ArgumentTypePurpose
subscribefn((callback) => unsubscribe)A function that subscribes the component to changes in the external store
getSnapshotfn() => anyReturns the current state (a snapshot) from the store
getServerSnapshot (optional)fn() => anyUsed during server-side rendering to get the state without access to the client

Example usage

Let's say we have a simple external store:

javascript
// store.js let listeners = []; let count = 0; export const store = { getSnapshot: () => count, subscribe: (callback) => { listeners.push(callback); return () => { listeners = listeners.filter(l => l !== callback); }; }, increment: () => { count++; listeners.forEach(l => l()); }, };

Now let's connect it to a React component:

javascript
import { useSyncExternalStore } from 'react'; import { store } from './store'; function Counter() { const count = useSyncExternalStore( store.subscribe, store.getSnapshot ); return ( <div> <p>Count: {count}</p> <button onClick={store.increment}>+</button> </div> ); }

Now the component:

  • reads state from the external store;
  • automatically re-renders when store.increment() changes the state;
  • works reliably under concurrent rendering and SSR.

Why this hook was introduced

Before React 18, libraries (Redux, Zustand, MobX, etc.) used unstable approaches like useEffect or useLayoutEffect to synchronize with external state.

This caused problems:

  • Under Concurrent Rendering, React could start a render with "stale" data;
  • Under SSR / Hydration, the client state could fail to match the server → warnings;
  • The subscription could be triggered at the wrong moment in the lifecycle.

useSyncExternalStore() solves all these problems.


How it works under the hood

React guarantees that:

  1. During render, React calls getSnapshot() and uses its value.
  2. If the external state changes during rendering, React repeats the render to reflect the current data.
  3. After mounting, React calls subscribe(listener) and watches for changes.
  4. When the state in the external store changes, React calls the listener, checks the new getSnapshot(), and if it differs, triggers an update.

The key point: React does this synchronously to avoid "desynchronization" between the data and the UI.


Example with Redux (internally)

React-Redux v8+ now uses useSyncExternalStore() internally instead of custom subscriptions, to guarantee correct behavior in React 18:

javascript
const state = useSyncExternalStore( store.subscribe, store.getState, store.getState // for SSR );

Previously, the Redux useSelector hook relied on useEffect → there could be lags and mismatches. Now everything is synchronous and predictable.


Example with window size (an external source - the browser)

javascript
function useWindowWidth() { return useSyncExternalStore( (callback) => { window.addEventListener('resize', callback); return () => window.removeEventListener('resize', callback); }, () => window.innerWidth ); } function Component() { const width = useWindowWidth(); return <div>Window width: {width}px</div>; }

Even if React "pauses" the render in Concurrent Mode, useSyncExternalStore guarantees that width always matches the current window state, with no "stale" values or glitches during hydration.


What the third argument (getServerSnapshot) does

This argument is used during server-side rendering (SSR): it lets you provide the state on the server when client data isn't available.

Example:

javascript
useSyncExternalStore( store.subscribe, store.getSnapshot, () => 0 // on the server we assume count = 0 );

This prevents hydration errors:

javascript
Warning: Text content did not match. Server: "0" Client: "5"

Important: don't confuse it with useEffect or useLayoutEffect

HookWhen it updatesSuitable for external state?
useEffectAsynchronously, after renderCan lag behind
useLayoutEffectSynchronously after commitBetter, but not SSR-compatible
useSyncExternalStoreSynchronously, within React's own cycleThe ideal choice

Summary

useSyncExternalStore() is a low-level hook that React uses to synchronize the UI with external data sources.

It guarantees:

  • correct behavior under Concurrent Rendering,
  • safety with SSR + Hydration,
  • no "desynchronization" between the store and the component.

In short

PropertyValue
ReturnsThe current state from the external source
Subscribes automaticallyYes
SSR-compatibleYes
Concurrent-safeYes
Replaces old patterns (useEffect for subscriptions)Yes
Main useRedux, Zustand, MobX, event listeners, custom stores

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.