It provides a safe and concurrent-friendly way to subscribe to external mutable sources and keep React in sync.
Before React 18, we often used:
useEffect + useState
But that approach:
useSyncExternalStoresolves these by ensuring React always reads a consistent snapshot of external state.
useSyncExternalStore APIconst value = useSyncExternalStore(subscribe, // tells React when to updategetSnapshot, // reads the current valuegetServerSnapshot // optional: for SSR)
Use it whenever state:
geolocationimport { useSyncExternalStore } from 'react'let location: GeolocationPosition | null = nullfunction subscribe(callback: () => void) {const id = navigator.geolocation.watchPosition(pos => {location = poscallback()})return () => navigator.geolocation.clearWatch(id)}function getSnapshot() {return location}function MyLocation() {const location = useSyncExternalStore(subscribe, getSnapshot)if (!location) return 'Location unavailable'return `${location.coords.latitude}, ${location.coords.longitude}`}
✔ React now re-renders whenever location updates ✔ No tearing, no stale values
matchMediaimport { useSyncExternalStore } from 'react'function makeMediaQueryStore(query: string) {const mql = window.matchMedia(query)return () =>useSyncExternalStore((cb) => {mql.addEventListener('change', cb)return () => mql.removeEventListener('change', cb)},() => mql.matches,() => false // SSR fallback)}const useNarrowScreen = makeMediaQueryStore('(max-width: 600px)')function App() {const isNarrow = useNarrowScreen()return <div>{isNarrow ? 'Narrow' : 'Wide'}</div>}
✔ React updates when the media query changes.
✔ Works correctly with concurrent rendering
Use useSyncExternalStore when: