Frontend Labs
⋯

72. Zustand: a state manager in 20 lines, and useSyncExternalStore

Zustand is one of the most-used React state libraries. This is its entire core, the published esm/vanilla.mjs of v5.0.15, unabridged:

const createStoreImpl = (createState) => {
  let state;
  const listeners = /* @__PURE__ */ new Set();
  const setState = (partial, replace) => {
    const nextState = typeof partial === "function" ? partial(state) : partial;
    if (!Object.is(nextState, state)) {
      const previousState = state;
      state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
      listeners.forEach((listener) => listener(state, previousState));
    }
  };
  const getState = () => state;
  const getInitialState = () => initialState;
  const subscribe = (listener) => {
    listeners.add(listener);
    return () => listeners.delete(listener);
  };
  const api = { setState, getState, getInitialState, subscribe };
  const initialState = state = createState(setState, getState, api);
  return api;
};

Everything in it is from Part I:

  • A closure (§6) holds state and listeners. There is no class and no this, so you can destructure the API freely.
  • Immutable updates by default. Object.assign({}, state, nextState) shallow-merges into a new object, so Object.is(prev, next) is a valid change check for React.
  • /* @__PURE__ */ tells bundlers that this new Set() has no side effects and can be dropped if unused (tree-shaking, §16).
  • initialState is declared after api but used in a closure above it. That's legal because getInitialState runs later, after the TDZ has ended (§4).

The React binding (esm/react.mjs) is about as short:

function useStore(api, selector = identity) {
  const slice = React.useSyncExternalStore(
    api.subscribe,
    React.useCallback(() => selector(api.getState()), [api, selector]),
    React.useCallback(() => selector(api.getInitialState()), [api, selector])
  );
  React.useDebugValue(slice);
  return slice;
}

useSyncExternalStore is React's official way to read from stores that live outside React. React calls subscribe, and on each notification it calls the snapshot function and re-renders only if the result changed by Object.is. That is why selectors matter: useStore(s => s.items.length) re-renders when the count changes, not when any other part of the store does. It also prevents tearing: with concurrent rendering, React could otherwise render half the tree with the old state and half with the new.

Pitfall. useStore(s => ({ a: s.a, b: s.b })) returns a new object on every call, so Object.is fails every time. That means an infinite re-render loop in v5, or a render on every store change in older versions. Select primitives, or wrap the selector with Zustand's useShallow.

Middleware is function composition

persist, devtools and immer are functions that take a state creator and return one, decorating set on the way. A working mini version:

const logger = (config) => (set, get, api) =>
  config((...args) => { const before = get(); set(...args); console.log("[log]", before, "→", get()); }, get, api);

const persist = (config, { name, storage }) => (set, get, api) => {
  const saved = storage.get(name);
  const initial = config((...args) => { set(...args); storage.set(name, get()); }, get, api);
  return saved ? { ...initial, ...saved } : initial;
};

const cart = createStore(logger(persist((set) => ({
  items: [],
  add: (item) => set((s) => ({ items: [...s.items, item] })),
}), { name: "cart", storage })));

// What useStore does, without React: notify only when the selected slice changes
const select = (store, selector, cb) => {
  let prev = selector(store.getState());
  return store.subscribe((s) => { const next = selector(s); if (!Object.is(next, prev)) cb((prev = next)); });
};
select(cart, (s) => s.items.length, (n) => console.log("badge:", n));
cart.getState().add("keyboard");   // badge: 1   [log] {"items":[]} → {"items":["keyboard"]}
cart.getState().add("mouse");      // badge: 2   …
cart.setState((s) => s);           // same object: Object.is bail-out, nobody notified
// a fresh store with the same persist() options starts with ["keyboard", "mouse"]

Generated from real-life-JS-guide.md · 8 Oct 2026 16:36