Suggest an editImprove this articleRefine the answer for “What does `useSyncExternalStore()` do?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`useSyncExternalStore()`** connects a React component to external state that React does not manage (for example, Redux, Zustand, an EventEmitter, a custom store, window.location, and so on). **Key point:** it guarantees that React always reads the current state snapshot and updates synchronously if the store changes during rendering.Shown above the full answer for quick recall.Answer (EN)Image## What `useSyncExternalStore()` does `useSyncExternalStore()` connects a React component to **external state** that **React does not manage** (for example, Redux, Zustand, an EventEmitter, a custom store, window.location, and so on). It provides: - **synchronous reading of the current value** (a store snapshot), - **subscription to updates**, - **correct behavior with concurrent rendering** (React 18) and **SSR**. > More simply: > `useSyncExternalStore()` lets React components **reliably read data from an external store** > and update when it changes. --- ## Syntax ```javascript const state = useSyncExternalStore( subscribe, // subscription function getSnapshot, // function that reads the current state getServerSnapshot? // (optional) version for SSR ); ``` --- ## Example: connecting to a custom store Say we have a simple store outside of React: ```javascript // store.js let listeners = []; let count = 0; export function increment() { count++; listeners.forEach((l) => l()); } export function subscribe(listener) { listeners.push(listener); return () => { listeners = listeners.filter((l) => l !== listener); }; } export function getSnapshot() { return count; } ``` Now let's connect it to React through `useSyncExternalStore`: ```javascript import { useSyncExternalStore } from "react"; import { subscribe, getSnapshot, increment } from "./store"; function Counter() { const count = useSyncExternalStore(subscribe, getSnapshot); return ( <div> <p>Count: {count}</p> <button onClick={increment}>+1</button> </div> ); } ``` Now: - The component gets the up-to-date value from `getSnapshot()` **immediately**; - When the store changes, `subscribe()` fires -> React re-renders the component; - Everything works **synchronously and predictably**, even in concurrent mode. --- ## What each argument does | Argument | What it does | |---|---| | `subscribe(listener)` | Registers a callback that is called when the state changes; returns an unsubscribe function | | `getSnapshot()` | Returns the **current value** of the state from the external source | | `getServerSnapshot()` *(optional)* | A version of `getSnapshot` for **SSR**, so the server and client stay in sync | --- ## Why this appeared in React 18 Previously, custom hooks like `useStore`, `useSelector`, and so on were used for this. But with the arrival of **Concurrent Rendering**, React started rendering several versions of the UI "at once". The problem with the old solutions: - They could pick up a "stale" value from the store, - Or cause "desynchronization" (the UI showed something different from what was actually in the store). `useSyncExternalStore` solves this, because it: - Guarantees that React always reads the **current snapshot** of the state, - And **updates synchronously** if the store changes during rendering. --- ## Example: integration with Redux Redux 8+ now uses `useSyncExternalStore` **internally**. ```javascript import { useSelector } from "react-redux"; function MyComponent() { const count = useSelector((state) => state.counter.value); return <div>{count}</div>; } ``` Inside, `useSelector` is implemented roughly like this: ```javascript function useSelector(selector) { return useSyncExternalStore( store.subscribe, () => selector(store.getState()) ); } ``` --- ## Example with window.location (an external source) ```javascript function useLocation() { return useSyncExternalStore( (callback) => { window.addEventListener("popstate", callback); return () => window.removeEventListener("popstate", callback); }, () => window.location.pathname ); } function RouterView() { const path = useLocation(); return <div>Current path: {path}</div>; } ``` React will automatically update the component when `window.location` changes. --- ## The SSR version (`getServerSnapshot`) With server-side rendering (Next.js, Remix, and so on) you can pass a 3rd argument: ```javascript useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); ``` This matters so that **the value on the server and the client match**, otherwise React will emit a "mismatched hydration" warning. --- ## When to use it | When | Why | |---|---| | You are connecting to an **external store** (Redux, Zustand, a custom store) | React does not manage this state | | You need a **reactive subscription** | The component must re-render when the store changes | | It matters to avoid desynchronization in Concurrent Rendering | `useSyncExternalStore` guarantees consistency | | You need safe behavior under SSR | You can pass `getServerSnapshot` | --- ## When you **don't** need it | Case | Why | |---|---| | You work with state through `useState` or `useReducer` | React manages the data itself - `useSyncExternalStore` is unnecessary | | You are making API requests | Use `useEffect` / React Query | | The store is local, not global | Overkill | --- ## Summary | Question | Answer | |---|---| | What does `useSyncExternalStore()` do | Lets a React component subscribe to external state (a store) and get up-to-date data | | Arguments | `(subscribe, getSnapshot, getServerSnapshot?)` | | Why it is needed | For state consistency under Concurrent Rendering and SSR | | Where it is used | Redux, Zustand, Jotai, custom stores, event emitters | | Do not use it for | Local React state (`useState`, `useReducer`) | --- **Main idea:** > `useSyncExternalStore()` is the "official way" to synchronously connect React to external state > without the risk of desynchronization between the store and the UI.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.