Composing Signals
Rimitive is built on composition. You pick the modules you need and compose() wires them together into a reactive service.
The simplest setup is just signals:
import { compose } from '@rimitive/core';import { SignalModule, ComputedModule, EffectModule } from '@rimitive/signals/extend';
const svc = compose(SignalModule, ComputedModule, EffectModule);compose() returns a use function with the primitives accessible on it:
const { signal, computed, effect } = svc;Using the Primitives
Section titled “Using the Primitives”Signals hold reactive values:
const count = signal(0);
count(); // read: 0count(1); // write: 1count(); // read: 1Computeds derive values from signals:
const doubled = computed(() => count() * 2);
doubled(); // 2 (count is 1)count(5);doubled(); // 10Effects run side effects when dependencies change:
effect(() => { console.log('Count is now:', count());});// logs: "Count is now: 5"
count(10);// logs: "Count is now: 10"Effects run synchronously — when a signal changes, effects execute immediately, not on the next tick. This makes reasoning about state straightforward: after a write, all effects have already run.
A Complete Example
Section titled “A Complete Example”import { compose } from '@rimitive/core';import { SignalModule, ComputedModule, EffectModule } from '@rimitive/signals/extend';
// Compose the modulesconst svc = compose(SignalModule, ComputedModule, EffectModule);const { signal, computed, effect } = svc;
// Create reactive stateconst firstName = signal('Ada');const lastName = signal('Lovelace');
// Derive valuesconst fullName = computed(() => `${firstName()} ${lastName()}`);
// React to changeseffect(() => { console.log('Full name:', fullName());});// logs: "Full name: Ada Lovelace"
// UpdatelastName('Byron');// logs: "Full name: Ada Byron"Adding More Modules
Section titled “Adding More Modules”Need batching? Add BatchModule:
import { BatchModule } from '@rimitive/signals/extend';
const svc = compose(SignalModule, ComputedModule, EffectModule, BatchModule);const { signal, effect, batch } = svc;
const a = signal(1);const b = signal(2);
effect(() => console.log(a() + b()));// logs: 3
batch(() => { a(10); b(20);});// logs: 30 (once, not twice)A Practical Example
Section titled “A Practical Example”Let’s build something real: a todo list with just signals.
import { compose } from '@rimitive/core';import { SignalModule, ComputedModule, EffectModule } from '@rimitive/signals/extend';
const { signal, computed, effect } = compose(SignalModule, ComputedModule, EffectModule);
// Stateconst items = signal<{ id: number; text: string; done: boolean }[]>([]);const filter = signal<'all' | 'active' | 'done'>('all');
// Derived stateconst filteredItems = computed(() => { const f = filter(); if (f === 'all') return items(); return items().filter(item => f === 'done' ? item.done : !item.done);});
const activeCount = computed(() => items().filter(item => !item.done).length);
// Actionslet nextId = 0;const addItem = (text: string) => { items([...items(), { id: nextId++, text, done: false }]);};
const toggleItem = (id: number) => { items(items().map(item => item.id === id ? { ...item, done: !item.done } : item ));};
const removeItem = (id: number) => { items(items().filter(item => item.id !== id));};
// React to changeseffect(() => { console.log(`${activeCount()} items left`);});
// Use itaddItem('Learn Rimitive');addItem('Build something');// logs: "2 items left"
toggleItem(0);// logs: "1 items left"State, derived values, actions, and reactions — all with just three primitives. This pattern works, but as your app grows you’ll want to extract reusable pieces. That’s where behaviors come in.
Why Compose?
Section titled “Why Compose?”You might wonder: why not just export these functions directly?
Composition gives you:
- Isolation — Each
compose()call creates an independent reactive context - Tree-shaking — Only bundle what you use
- Extensibility — Add view modules, router, custom modules later
- Testing — Create fresh contexts per test
This is the foundation. Everything else in Rimitive builds on top of compose().