Suggest an editImprove this articleRefine the answer for “What does the useSyncExternalStore() hook do?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`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 under concurrent rendering or server-side rendering (SSR). **Key point:** it reads the state synchronously during render and re-renders if the external data changed, avoiding desynchronization between the store and the component.Shown above the full answer for quick recall.Answer (EN)Image## 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 ); ``` | 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: ```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 | 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 |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.