Skip to content
ripple logoRippleState
Framework-agnostic signals, derived values, effects, scopes, watchers, and async resources.
Version
v2.2.1
Size
2.6 KB gzip
Dependencies
Zero dependencies
BrowserNode ≥22SSRDeno
createRipplesignalcomputedeffectbatch View all 10 exports

Why Ripple?

Hand-rolled reactive state spreads subscription, cleanup, and derived-value rules across application code. Ripple gives you one graph boundary with explicit disposal and fine-grained dependencies while keeping rendering and routing outside the runtime.

ts
// Before
let count = 0;
const listeners = new Set<() => void>();

function setCount(next: number) {
  count = next;
  for (const listener of listeners) listener();
}

// After
import { createRipple } from '@vielzeug/ripple';

const ripple = createRipple();
const count = ripple.signal(0);
const doubled = ripple.computed(() => count.value * 2);
const stop = ripple.effect(() => console.log(doubled.value));

count.value = 1;
stop.dispose();
ripple.dispose();
FeatureRippleZustandJotai
Bundle size2.6 KB~3.5 kB~7 kB
Zero dependencies
Framework-agnosticReact-first
Explicit graph lifetime
Fine-grained derived valuesSelectorsAtoms

Use Ripple when you need framework-independent state with explicit graph lifetime and small composable primitives.

Consider a framework store when component bindings, server cache, or framework-specific tooling matter more than portable reactive state.

Installation

sh
pnpm add @vielzeug/ripple
sh
npm install @vielzeug/ripple
sh
yarn add @vielzeug/ripple

Quick Start

Create one graph, derive a value, observe it, then dispose resources when the graph lifetime ends.

ts
import { createRipple } from '@vielzeug/ripple';

const ripple = createRipple();
const count = ripple.signal(0);
const doubled = ripple.computed(() => count.value * 2);
const stop = ripple.effect(() => console.log(doubled.value));

ripple.batch(() => {
  count.value = 1;
  count.value = 2;
});

stop.dispose();
ripple.dispose();

Features

  • createRipple() creates an isolated graph and lifetime boundary.
  • signal() stores writable values with configurable equality.
  • computed() derives lazy read-only values.
  • effect() reacts to dependency changes with cleanup support.
  • batch() coalesces synchronous writes and notifications.
  • createScope() groups owned reactive work.
  • watch() observes one selected source transition.
  • resource() loads async values with stale-work cancellation.

Documentation

See Also

  • Ore — uses Ripple signals and effects for web-component reactivity.
  • Clockwork — exposes machine state through reactive Ripple values.
  • Ledger — adds command-based undo and redo beside Ripple state.