NgRx Signals
@homeostate/store-ngrx-signals
Keep a slice of an Angular NgRx signalState or signalStore in sync with a Yjs, Loro or Automerge document.
NgRx Signals adapter for
@homeostate/core. It keeps a slice of a signalState or
signalStore in sync with a CRDT backend such as
Yjs, Loro or
Automerge.
Install
npm install @homeostate/core @homeostate/store-ngrx-signals @ngrx/signals @homeostate/crdt-yjs yjs@angular/core 21 and @ngrx/signals 21 are peer dependencies.
Usage
injector is required: it owns the state watcher, so the engine can reconnect outside an
injection context. Capture it with inject(Injector):
import { DestroyRef, inject, Injector } from "@angular/core";
import { patchState, signalState } from "@ngrx/signals";
import { createSyncEngine } from "@homeostate/core";
import { createNgrxSignalsAdapter } from "@homeostate/store-ngrx-signals";
import { createYjsBackend } from "@homeostate/crdt-yjs";
import * as Y from "yjs";
const state = signalState({ shared: { count: 0 }, panelOpen: false });
const doc = new Y.Doc();
const adapter = createNgrxSignalsAdapter(state, {
select: (state) => state.shared,
replace: (shared) => patchState(state, { shared }),
injector: inject(Injector),
});
const engine = createSyncEngine(createYjsBackend(doc, "counter"), adapter);
engine.connect();
patchState(state, ({ shared }) => ({ shared: { count: shared.count + 1 } }));
inject(DestroyRef).onDestroy(() => {
engine.disconnect();
doc.destroy();
});Attach a Yjs provider to doc for replication, and destroy it on teardown too.
selectmust return the same object until the slice changes, so update the slice immutably and never build a new object inselect.replacemust replace the slice completely, including removing absent keys.patchStateshallow-merges, so keep synced data under one key, such asshared, and replace that key.- For a protected
signalStore, create the adapter insidewithHooksorwithMethods, or pass a public replacement method asreplace.
Disconnecting the engine, or destroying the injector, removes the watcher. Disconnecting the engine and destroying the document stay yours to do.
See the Angular playground for a complete demo.