Skip to the solution
HardFrontend7 min read

What problem does useSyncExternalStore solve?

938 views

The problem

Question presented to candidate: "What is useSyncExternalStore for, and when would you actually reach for it?"

What a strong answer should cover:

  • It is the official way to subscribe to state that lives outside React β€” a module store, a browser API, a websocket cache β€” and read it safely during rendering.
  • Two problems it solves at once: staleness (React never learns a plain variable changed) and tearing (an interruptible render reading a mutable value at two different moments).
  • The three arguments: subscribe, getSnapshot, and getServerSnapshot for SSR.
  • getSnapshot must return a cached value β€” a fresh object each call is an infinite loop, with React warning that the result should be cached.
  • subscribe must be stable, or React resubscribes on every render.
  • The deliberate cost: updates from an external store are synchronous and non-interruptible, which is the price of consistency.
  • You rarely write it directly β€” Redux, Zustand, and Jotai all call it internally. You reach for it when integrating a store or browser API yourself.
  • Genuine direct uses: matchMedia, navigator.onLine, localStorage sync across tabs, and a third-party non-React library.
  • Before React 18, libraries hand-rolled this and could tear under concurrent rendering.

Clarifying questions expected:

  • "Is the state actually outside React, or could it just be React state lifted up?"
  • "Is this server-rendered?" β€” that decides whether getServerSnapshot is mandatory.

Code / implementation expected: Yes β€” a small store with a correctly cached snapshot, and a browser-API subscription.

  • react
  • concurrent
  • state-management

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

7 min read Β· 10 sections

Try it in the playground

Open any example in the playground to run and change it.

ReactA module store and a browser API, both read through the hook
import { useSyncExternalStore, useState, useCallback } from "react";

// ── A tiny external store. Note the snapshot is the state object itself,
//    replaced only on a write β€” that is what makes getSnapshot cacheable. ───
function createStore(initial) {
  let state = initial;
  const listeners = new Set();
  return {
    getSnapshot: () => state,                       // SAME reference until a write
    subscribe: (l) => { listeners.add(l); return () => listeners.delete(l); },
    setState: (patch) => {
      state = { ...state, ...patch };               // new object -> Object.is sees a change
      listeners.forEach((l) => l());
    },
  };
}

const cartStore = createStore({ items: 0 });

// WRONG, for contrast β€” a fresh object every call means Object.is is always
// false, so React re-renders forever:
//   getSnapshot: () => ({ items: state.items })

function CartBadge({ label }) {
  const cart = useSyncExternalStore(
    cartStore.subscribe,
    cartStore.getSnapshot,
    cartStore.getSnapshot,   // getServerSnapshot β€” required under SSR
  );
  return (
    <p style={{ margin: "4px 0" }}>
      <code style={{ display: "inline-block", minWidth: 110 }}>{label}</code>
      items: <strong>{cart.items}</strong>
    </p>
  );
}

// ── A browser API React knows nothing about. This is the case where you
//    genuinely write the hook yourself rather than reaching for a library. ──
const onlineStore = {
  subscribe(callback) {
    window.addEventListener("online", callback);
    window.addEventListener("offline", callback);
    return () => {
      window.removeEventListener("online", callback);
      window.removeEventListener("offline", callback);
    };
  },
  getSnapshot: () => navigator.onLine,              // a boolean β€” always cacheable
  getServerSnapshot: () => true,                    // assume online when rendering on the server
};

function useOnlineStatus() {
  return useSyncExternalStore(
    onlineStore.subscribe,
    onlineStore.getSnapshot,
    onlineStore.getServerSnapshot,
  );
}

export default function App() {
  const isOnline = useOnlineStatus();
  const [, force] = useState(0);

  // Mutating the store from OUTSIDE React entirely β€” no setState involved.
  const add = useCallback(() => {
    cartStore.setState({ items: cartStore.getSnapshot().items + 1 });
  }, []);

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6, maxWidth: 440 }}>
      <h4 style={{ marginTop: 0 }}>Two independent readers, one store</h4>
      <CartBadge label="header" />
      <CartBadge label="sidebar" />

      <p>
        <button onClick={add}>add an item (mutates the store directly)</button>{" "}
        <button onClick={() => force((n) => n + 1)}>re-render the app</button>
      </p>

      <p style={{ padding: 8, borderRadius: 6, background: isOnline ? "#e7f7e9" : "#fdecea" }}>
        Browser connectivity via useSyncExternalStore:{" "}
        <strong>{isOnline ? "online" : "offline"}</strong>
        <br />
        <span style={{ fontSize: 13, color: "#666" }}>
          Toggle your network β€” or devtools offline mode β€” and this updates
          without any React state involved.
        </span>
      </p>

      <p style={{ color: "#666", fontSize: 13 }}>
        Both badges always show the same number: React re-checks the snapshot
        before committing, so they cannot disagree.
      </p>
    </div>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 77 of 119 in React.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All React.js questions
Up nextHard

How do you optimize React component rendering?

Continue