React hooks that bridge Yjs CRDT state into React.
Read a shared document's live state with useYMap / useYText / useYDoc and
render presence with useAwareness — no hand-rolled observe/unobserve loops, no
forceUpdate, no stale reads. Updates are batched per microtask and the hooks
are read-only by contract, so your render path stays clean and your mutations
stay explicit.
It's the reactivity layer behind Brainstorm, extracted to stand on its own.
peer deps:
react · yjs · zero runtime dependencies
npm install @brainstorm-os/react-yjs yjs reactimport * as Y from "yjs";
import { useYMap, useYText } from "@brainstorm-os/react-yjs";
const doc = new Y.Doc();
const meta = doc.getMap("meta");
function Title() {
// Re-renders only when `meta` changes; returns a plain snapshot object.
const { title } = useYMap(meta);
return <h1>{title ?? "Untitled"}</h1>;
}
function Body({ text }: { text: Y.Text }) {
const value = useYText(text); // live string
return <p>{value}</p>;
}Mutations flow through Yjs as usual (meta.set("title", "…")) — the hooks
observe and re-render; they never write.
Value hooks — subscribe to a single shared type and get a snapshot back:
| Hook | Reads |
|---|---|
useYMap(map) |
a Y.Map as a plain object |
useYText(text) |
a Y.Text as a string |
useYXmlFragment(frag) |
a Y.XmlFragment change signal |
useYDoc(docOrId) |
a whole Y.Doc (+ useYDocLoaded / useYDocApplyPending) |
Presence & awareness — useAwareness for React, plus a framework-free
core: createLocalAwareness, createSyncedAwareness, createPresenceTransport,
and createLoopbackTransports for tests.
Document resolution — YDocProvider + createYDocResolver let a host map a
stable entity id to a lazily-loaded, transport-synced Y.Doc, so components can
call useYDoc(id) without knowing where the doc comes from.
Framework-free stores — every hook is a thin useSyncExternalStore wrapper
over a pure store you can drive without React: createYStore, yMapStore,
yTextStore, yDocStore, and the generic createQueryStore for async lists.
Use these in vanilla-DOM views, workers, or tests.
Live entity lists — useLiveEntities binds any { list(), onChange? }
source to React with debouncing and structural short-circuiting. useVaultEntities
is the Brainstorm-flavored specialization; both expect a small host contract
described in src/brainstorm-types.ts — provide any
object matching those shapes to use them outside Brainstorm.
react (^19) and yjs (^13.6.30) are peer dependencies — the library brings no
copy of its own. react is marked optional: the *Store builders and the
awareness/presence core work in a non-React runtime.
bun install # or npm install
bun run test # vitest — 99 tests
bun run typecheck
bun run build # tsup → dist/ (ESM + .d.ts)MIT © Brainstorm OS. Part of the Brainstorm project.