← Back to posts

Understanding useSyncExternalStore in React (with a localStorage persistence example)

Published: January 8, 2026

useSyncExternalStore

Most developers never touch it directly — but almost every modern state management library depends on it.

If you’ve ever wondered:

— this hook is the missing piece.

In this article, we’ll cover:

What is useSyncExternalStore?

useSyncExternalStore is a React hook designed to let React safely subscribe to external state sources.

External state means any state that does not live inside React.

Examples:

It allows React to:

This hook is the official low-level API for integrating external stores with React.

Why does this hook exist?

Before React 18, people usually wrote code like this:

TypeScript
1useEffect(() => store.subscribe(forceUpdate), []);

This works in simple apps, but it breaks under concurrent rendering because React may:

useSyncExternalStore solves this by letting React control:

This makes external stores concurrency-safe.

The API in one look

TypeScript
1const value = useSyncExternalStore(
2  subscribe,
3  getSnapshot,
4  getServerSnapshot // optional (SSR)
5);

Where:

Whenever the snapshot changes, React re-renders.

Using useSyncExternalStore with localStorage

localStorage is a perfect example of an external store: it lives outside React, persists across reloads, and can change from other tabs.

Let’s build a small reactive wrapper around it.

Step 1: Create a localStorage store

TypeScript
1function createLocalStorageStore(key, initialValue) {
2  const listeners = new Set();
3
4  const getSnapshot = () => {
5    const data = localStorage.getItem(key);
6    return data ? JSON.parse(data) : initialValue;
7  };
8
9  const setValue = (value) => {
10    localStorage.setItem(key, JSON.stringify(value));
11    listeners.forEach((l) => l());
12  };
13
14  const subscribe = (listener) => {
15    listeners.add(listener);
16
17    const onStorage = (e) => {
18      if (e.key === key) listener();
19    };
20
21    window.addEventListener("storage", onStorage);
22
23    return () => {
24      listeners.delete(listener);
25      window.removeEventListener("storage", onStorage);
26    };
27  };
28
29  return { getSnapshot, setValue, subscribe };
30}

This object behaves like a tiny external store:

It also listens for the browser’s storage event, so multiple tabs stay in sync.

Step 2: Create a hook using useSyncExternalStore

TypeScript
1import { useSyncExternalStore } from "react";
2
3function useExternalStore(store) {
4  return useSyncExternalStore(
5    store.subscribe,
6    store.getSnapshot,
7    store.getSnapshot
8  );
9}

This hook connects React to our external store.

Step 3: Use it in a component

TypeScript
1const counterStore = createLocalStorageStore("counter", 0);
2
3function Counter() {
4  const count = useExternalStore(counterStore);
5
6  return (
7    <div>
8      <p>Count: {count}</p>
9      <button onClick={() => counterStore.setValue(count + 1)}>
10        Increment
11      </button>
12    </div>
13  );
14}

Now your app has:

All powered by useSyncExternalStore.

How this compares to useState + useEffect

TypeScript
1const [value, setValue] = useState(() => {
2  return JSON.parse(localStorage.getItem("key")) ?? 0;
3});
4
5useEffect(() => {
6  localStorage.setItem("key", JSON.stringify(value));
7}, [value]);

This is perfectly fine for local component state.

But it:

useSyncExternalStore is built specifically to solve those problems.

How this fits into modern state management

Most modern libraries combine two ideas:

Proxy systems decide what changed.
useSyncExternalStore tells React how and when to re-render.

That’s why this hook is foundational to modern React ecosystems.

When should you use it?

Use it when:

You probably don’t need it for normal component state.

Final takeaway

useSyncExternalStore is React’s official bridge between:

👉 React’s rendering engine
👉 and the outside world

It allows you to build:

If useState is React’s local state primitive,
useSyncExternalStore is React’s external state primitive.