@vielzeug/ripple
v1.2.1
Published
Fine-grained reactive state — signals, computed values, effects, stores, and reactive scopes
Readme
@vielzeug/ripple
Tiny, type-safe reactive primitives — signals, effects, computed values, and object stores. Zero dependencies, works everywhere.
Package: @vielzeug/ripple · Category: State
Key exports: signal, computed, effect, effectAsync, resource, watch, batch, store, storeWithHistory, untrack, scope, onCleanup, readonly, isSignal, isComputed, isStore
When to use: Fine-grained reactivity without a framework. Powers Ore templates. Works in any TS/JS environment including Node, Deno, and SSR.
Related: @vielzeug/ore · @vielzeug/forge · @vielzeug/herald
@vielzeug/ripple is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output.
Installation
# pnpm
pnpm add @vielzeug/ripple
# npm
npm install @vielzeug/ripple
# yarn
yarn add @vielzeug/rippleSub-paths
| Import | Purpose |
| --------------------------- | ------------------------------------------------------------------------ |
| @vielzeug/ripple | Core primitives and types |
| @vielzeug/ripple/devtools | installDevTools, debugEffect — dev-only, tree-shaken from production |
| @vielzeug/ripple/ssr | SSR tracking isolation helpers (setTrackingProvider, createAsyncProvider, withProvider, runWithProvider). Node.js only — do not import in browser builds. |
Quick Start
import { signal, computed, effect, store, watch, batch } from '@vielzeug/ripple';
// Signals
const count = signal(0);
const doubled = computed(() => count.value * 2); // Computed<number>
const sub = effect(() => {
console.log('doubled:', doubled.value); // re-runs when count changes
});
count.value = 5; // → logs "doubled: 10"
sub.dispose();
doubled.dispose();
// Stores with typed lenses
const cart = store({ items: 0, label: 'empty' });
const items = cart.lens('items'); // Signal<number> — cached, path-scoped
items.value = 3;
console.log(cart.value); // { items: 3, label: 'empty' }
cart.replace((s) => ({ ...s, label: 'checkout' })); // replace entire state via fn
cart.reset();
// Effect options — scheduler, name
const stop = effect(() => console.log('items:', items.value), { scheduler: 'microtask', name: 'cart-logger' });
stop.dispose();
// Async resource — tracks deps and re-runs on change
import { resource } from '@vielzeug/ripple';
const userId = signal('u1');
const userState = resource(async (signal) => {
const id = userId.value; // tracked dep
return fetch(`/users/${id}`, { signal }).then((r) => r.json());
});
// userState.value → { status: 'loading' | 'ready' | 'error', data, error }
// Store with undo/redo history
import { storeWithHistory } from '@vielzeug/ripple';
const editor = storeWithHistory({ text: '' }, { maxHistory: 50 });
editor.patch({ text: 'hello' });
editor.patch({ text: 'hello world' });
editor.undo(); // back to 'hello'
editor.redo(); // forward to 'hello world'Documentation
License
MIT © Helmuth Saatkamp — part of the Vielzeug monorepo.
