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
const snapshot = useSyncExternalStore(
subscribe,
getSnapshot,
getServerSnapshot? // optional, for SSR
);| Argument | Type | Purpose |
|---|---|---|
subscribe | fn((callback) => unsubscribe) | A function that subscribes the component to changes in the external store |
getSnapshot | fn() => any | Returns the current state (a snapshot) from the store |
getServerSnapshot (optional) | fn() => any | Used during server-side rendering to get the state without access to the client |
Example usage
Let's say we have a simple external store:
// 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:
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:
- During render, React calls
getSnapshot()and uses its value. - If the external state changes during rendering, React repeats the render to reflect the current data.
- After mounting, React calls
subscribe(listener)and watches for changes. - 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:
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)
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:
useSyncExternalStore(
store.subscribe,
store.getSnapshot,
() => 0 // on the server we assume count = 0
);This prevents hydration errors:
Warning: Text content did not match. Server: "0" Client: "5"Important: don't confuse it with useEffect or useLayoutEffect
| Hook | When it updates | Suitable for external state? |
|---|---|---|
useEffect | Asynchronously, after render | Can lag behind |
useLayoutEffect | Synchronously after commit | Better, but not SSR-compatible |
useSyncExternalStore | Synchronously, within React's own cycle | The 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
| Property | Value |
|---|---|
| Returns | The current state from the external source |
| Subscribes automatically | Yes |
| SSR-compatible | Yes |
| Concurrent-safe | Yes |
Replaces old patterns (useEffect for subscriptions) | Yes |
| Main use | Redux, Zustand, MobX, event listeners, custom stores |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.