Build Your Own useSyncExternalStore for React State Subscriptions
React’s useSyncExternalStore hook is designed to connect React components to external sources of truth—like localStorage, WebSocket streams, or even Redux-style stores. While powerful, its internal magic can be hard to grasp. Let’s demystify it by building a simplified version from scratch. Step 1: Set Up a Basic Store This store will manage state outside of React and notify listeners when updates occur. function createStore(initialValue) { let value = initialValue; const listeners = new Set(); return { getSnapshot: () => value, setValue: (newVal) => { value = newVal; listeners.forEach((l) => l()); }, subscribe: (listener) => { listeners.add(listener); return () => listeners.delete(listener); }, }; } Step 2: Build a Custom Hook This custom hook mimics the signature of useSyncExternalStore. import { useEffect, useSyncExternalStore } from "react"; function useCustomStore(store) { return useSyncExternalStore( store.subscribe, store.getSnapshot ); } Step 3: Use the Store in a Component Let’s create a counter component that subscribes to the store and reacts to changes instantly. const counterStore = createStore(0); function Counter() { const count = useCustomStore(counterStore); return ( Count: {count} counterStore.setValue(count + 1)}>+1 counterStore.setValue(count - 1)}>-1 ); } ✅ Pros Clean separation between state logic and UI. Supports external systems like WebSockets, browser APIs, etc. Fully concurrent-mode safe (when using useSyncExternalStore). ⚠️ Cons Still requires you to manage the store manually (no reducer helpers, etc.). Harder to debug compared to using React state directly. Won’t work in older versions of React (prior to 18). Wrap-Up If you’ve ever wanted to connect React to a global event system or shared non-React data, useSyncExternalStore is the right tool—and now you know how to build it yourself. Try swapping in your own state containers, browser listeners, or even signals. If this was helpful, you can support me here: Buy Me a Coffee ☕
React’s useSyncExternalStore
hook is designed to connect React components to external sources of truth—like localStorage, WebSocket streams, or even Redux-style stores. While powerful, its internal magic can be hard to grasp. Let’s demystify it by building a simplified version from scratch.
Step 1: Set Up a Basic Store
This store will manage state outside of React and notify listeners when updates occur.
function createStore(initialValue) {
let value = initialValue;
const listeners = new Set();
return {
getSnapshot: () => value,
setValue: (newVal) => {
value = newVal;
listeners.forEach((l) => l());
},
subscribe: (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
},
};
}
Step 2: Build a Custom Hook
This custom hook mimics the signature of useSyncExternalStore
.
import { useEffect, useSyncExternalStore } from "react";
function useCustomStore(store) {
return useSyncExternalStore(
store.subscribe,
store.getSnapshot
);
}
Step 3: Use the Store in a Component
Let’s create a counter component that subscribes to the store and reacts to changes instantly.
const counterStore = createStore(0);
function Counter() {
const count = useCustomStore(counterStore);
return (
Count: {count}
);
}
✅ Pros
- Clean separation between state logic and UI.
- Supports external systems like WebSockets, browser APIs, etc.
- Fully concurrent-mode safe (when using
useSyncExternalStore
).
⚠️ Cons
- Still requires you to manage the store manually (no reducer helpers, etc.).
- Harder to debug compared to using React state directly.
- Won’t work in older versions of React (prior to 18).
Wrap-Up
If you’ve ever wanted to connect React to a global event system or shared non-React data, useSyncExternalStore
is the right tool—and now you know how to build it yourself. Try swapping in your own state containers, browser listeners, or even signals.
If this was helpful, you can support me here: Buy Me a Coffee ☕