createContext: scoped store provider factory
At a glance
- One export:
createContext— a factory pairing a Zustand store with a scopedProvider/useStore. - Used by
file-viewerfor its per-instance store. - Not used by
plugins— its state is a genuinely global singleton instead. - Calling
useStoreoutside itsProviderthrows immediately, it never returnsundefined.
packages/context exports exactly one thing: createContext. It's a factory that takes a function for building a Zustand store, and returns a matched Provider / useStore pair, scoped to whatever subtree the Provider wraps.
The problem it solves
A plain Zustand create() call gives you one store for the lifetime of the module — a singleton. That's fine for state that really is global (see how plugins does this below), but wrong for state that should be reset or duplicated per mounted instance — for example, one file-viewer store per file being viewed, not one store shared by every file-viewer on the page.
createContext closes over a createStore function you provide, and lazily constructs one store instance per Provider mount (via a useRef, so it survives re-renders but not remounts). It then hands back:
Provider— a component that takes your store's init props pluschildren, builds the store on first render, and puts it on a React Context.useStore— a hook, usable by any descendant ofProvider, that reads from that Context and subscribes to the store with the same(selector, equalityFn)signature Zustand's own hooks use. Calling it outside aProviderthrows immediately rather than silently returningundefined.
Who uses it
file-viewer is the one package here that builds its state this way: context/file.ts calls createContext with a function that builds the file-viewer's { ui, file, path } store (createPackageStore), and exports the result as FileProvider / useStore. Each <FileProvider> in the app gets its own independent store — which is what you want when more than one file view could be mounted at once.
Contrast this with plugins, which does not use createContext: it keeps one global Zustand store (useGlobalPluginStore, built with a plain create() call) shared across the whole app, plus a separate plain React.createContext for a bag of host callback functions (PluginProviderCtx) that plugins call into. That split is deliberate there — plugin state genuinely is global (which plugins are registered, their logs, their progress), so there's no per-subtree instance to scope.
Rule of thumb
If a package's state should exist once per mounted instance of something, reach for @morph-mapper/context. If it's truly singleton, a plain Zustand create() is simpler — that's what plugins does.