What is useSyncExternalStore in React?
What is useSyncExternalStore?
useSyncExternalStore is a React 18+ hook for subscribing to external stores β data sources that exist outside React (browser APIs, third-party state libraries, global variables). It ensures consistent reads during concurrent rendering.
Why It's Needed
With concurrent rendering, React may "pause" and "resume" rendering. Without useSyncExternalStore, you might read different values of an external store during a single render β a bug called tearing.
Basic API
const value = useSyncExternalStore(
subscribe, // Function to subscribe to changes
getSnapshot, // Function to get current value
getServerSnapshot // Optional: value for SSR
);Example: Online Status
function useOnlineStatus() {
return useSyncExternalStore(
// subscribe β called when store changes
(callback) => {
window.addEventListener("online", callback);
window.addEventListener("offline", callback);
return () => {
window.removeEventListener("online", callback);
window.removeEventListener("offline", callback);
};
},
// getSnapshot β returns current value
() => navigator.onLine,
// getServerSnapshot β value for SSR
() => true
);
}
function StatusBar() {
const isOnline = useOnlineStatus();
return <div>{isOnline ? "π’ Online" : "π΄ Offline"}</div>;
}Example: Window Width
function useWindowWidth() {
return useSyncExternalStore(
(callback) => {
window.addEventListener("resize", callback);
return () => window.removeEventListener("resize", callback);
},
() => window.innerWidth,
() => 1024 // Server default
);
}
function Layout() {
const width = useWindowWidth();
return width > 768 ? <DesktopLayout /> : <MobileLayout />;
}Example: localStorage
function useLocalStorage<T>(key: string, initialValue: T) {
const subscribe = (callback: () => void) => {
window.addEventListener("storage", callback);
return () => window.removeEventListener("storage", callback);
};
const getSnapshot = () => {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
};
const getServerSnapshot = () => initialValue;
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
}
// Usage
function Settings() {
const theme = useLocalStorage("theme", "light");
return <div>Current theme: {theme}</div>;
}How State Libraries Use It
Libraries like Zustand and Redux internally use useSyncExternalStore:
// Simplified Zustand implementation concept
function useStore<T>(store: Store<T>, selector: (state: T) => any) {
return useSyncExternalStore(
store.subscribe,
() => selector(store.getState()),
() => selector(store.getInitialState())
);
}When to Use
| Scenario | Use |
|---|---|
| React state (useState, useReducer) | β Not needed |
| Browser APIs (online, resize, media query) | β useSyncExternalStore |
| Third-party stores (without React bindings) | β useSyncExternalStore |
| Building a state management library | β useSyncExternalStore |
| Reading from localStorage/sessionStorage | β useSyncExternalStore |
Important:
useSyncExternalStore prevents tearing in concurrent React by ensuring consistent reads from external data sources. Use it for browser APIs, third-party stores, and any non-React state. If you're using React state (useState/useReducer), you don't need it β React handles those correctly already.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.