React 18 introduced a low-level hook that most developers never touch directly, but almost every modern state library depends on:
useSyncExternalStore
It is React’s official way to connect components to external state sources — state that lives outside React.
Examples:
localStorage- Redux / Zustand stores
- browser APIs
- WebSocket data
What problem does it solve?
Before React 18, we usually subscribed to stores like this:
useEffect(() => store.subscribe(forceUpdate), []);
This breaks under concurrent rendering.
useSyncExternalStore fixes this by letting React control how it:
- subscribes to changes
- reads the current value
- triggers re-renders safely
The API
const value = useSyncExternalStore(
subscribe,
getSnapshot,
getServerSnapshot?
);
-
subscribe→ how React listens for updates -
getSnapshot→ how React reads current state
When the snapshot changes, React re-renders.
Example: persistent state with localStorage
1. Create a tiny external store
function createLocalStorageStore(key, initialValue) {
const listeners = new Set();
const getSnapshot = () => {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : initialValue;
};
const setValue = (value) => {
localStorage.setItem(key, JSON.stringify(value));
listeners.forEach((l) => l());
};
const subscribe = (listener) => {
listeners.add(listener);
const onStorage = (e) => {
if (e.key === key) listener();
};
window.addEventListener("storage", onStorage);
return () => {
listeners.delete(listener);
window.removeEventListener("storage", onStorage);
};
};
return { getSnapshot, setValue, subscribe };
}
2. Connect it to React
import { useSyncExternalStore } from "react";
function useExternalStore(store) {
return useSyncExternalStore(
store.subscribe,
store.getSnapshot,
store.getSnapshot
);
}
3. Use it
const counterStore = createLocalStorageStore("counter", 0);
function Counter() {
const count = useExternalStore(counterStore);
return (
<>
<p>Count: {count}</p>
<button onClick={() => counterStore.setValue(count + 1)}>
+
</button>
</>
);
}
Now your state is:
- persistent
- shared
- cross-tab synced
- concurrent-safe
When should you use this?
Use useSyncExternalStore when:
- state lives outside React
- many components share the same data
- you’re building a store, persistence layer, or library
For normal component state, useState is still the right tool.
Final takeaway
useState manages React-owned state.
useSyncExternalStore connects React to the outside world.
If you’re building anything beyond local component state, this hook is the foundation.
Top comments (1)
Does this really work? The "storage" event is supposedly only sent to other open Tabs and not the currently open one.