Building a Custom Store with useSyncExternalStore
Implement a lightweight external store from scratch and connect it to React with useSyncExternalStore.
Building a Custom Store with useSyncExternalStore is a free React Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Minimal Store Requirements
A custom store needs three things: some state, a subscribe method to register listeners, and a getSnapshot method to read the current state. That is the full contract useSyncExternalStore expects.
Keeping the store this small makes it framework-agnostic, so the same store can be used inside or outside React.
A createStore Factory
A createStore factory takes an initial value and returns an object with getSnapshot, subscribe, and an update method. Internally it holds the current state and a set of listener callbacks.
This factory pattern lets you spin up multiple independent stores with a single reusable function.
The update Function Notifies Subscribers
When update changes the state, it sets the new value and then calls every registered listener. Those listeners are the callbacks React provided through subscribe.
Calling them tells React the store changed, prompting it to re-read getSnapshot and re-render any components reading from the store.
Connecting to React via the Hook
A component reads from the store by calling useSyncExternalStore with the store subscribe and getSnapshot methods. The hook returns the current value and re-renders when it changes.
You can wrap this in a custom hook like useStore so components call a single function instead of wiring the hook arguments themselves each time.
Sharing Across Components Without a Provider
Because the store is a module-level object, any component that imports it shares the same state, no Context Provider required. There is no tree to wrap and no prop drilling.
This is a key appeal of external stores: shared global state with a flat import-and-use model rather than a Provider hierarchy.
Multiple Stores for Slices
You can create several small stores, each owning a slice of state, instead of one monolithic store. Components subscribe only to the slices they care about.
This keeps concerns separated and limits re-renders, since changing one slice never disturbs components reading from a different one.
Zustand Uses It Internally
Zustand, a popular minimal state library, is built on exactly this pattern and uses useSyncExternalStore internally since version 4. Its tiny API is essentially a polished createStore with a selector layer.
Seeing this connection demystifies such libraries: they are thin, ergonomic wrappers over the same primitive you can build yourself.
Re-rendering Only on Changed Slices
By passing a selector to getSnapshot that returns just the portion of state a component uses, you ensure the component re-renders only when that portion changes.
This requires a stable selector result, so memoize derived values to avoid the new-reference infinite loop while still scoping updates to the relevant slice.
A Devtools Debug Label
For debugging you can attach a label to a store and log updates, or integrate with browser devtools so you can trace which store changed and when.
Even a simple console log inside update, gated behind a development flag, helps you understand state flow while building the store.
Testing the Store Inside and Outside React
Because the store is plain JavaScript, you can test its logic directly: call update and assert getSnapshot returns the new value, without rendering anything.
You can also test it inside React by rendering a component that reads from it and verifying re-renders happen after update, covering both the store and its integration.
Quick Check: Custom Store subscribe Function
Recall the contract of a custom store subscribe method.
Recap: Building a Custom Store
A custom store needs state, subscribe, and getSnapshot. A createStore factory plus an update that notifies listeners gives shared global state with no Provider, since module imports share one instance.
Use multiple slice stores and selectors to limit re-renders, remembering stable references. Zustand v4+ is built on this exact pattern, and the plain-JS store is easy to test in and out of React.
Frequently asked questions
Is the “Building a Custom Store with useSyncExternalStore” lesson free?
Yes — the full text of “Building a Custom Store with useSyncExternalStore” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.
What will I learn in “Building a Custom Store with useSyncExternalStore”?
Implement a lightweight external store from scratch and connect it to React with useSyncExternalStore. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start React Academy?
No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Building a Custom Store with useSyncExternalStore” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this React Academy lesson?
Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- The Problem with External Store Subscriptions
- useSyncExternalStore API and Parameters
- Subscribing to Browser APIs
- Building a Custom Store with useSyncExternalStore