Reactive Core

Signals#

TC39-aligned, glitch-free diamond propagation, custom equality

Computed#

lazy, cached, version-tracked

Effects#

auto-tracking, auto-cleanup via scopes, optional cleanup callback (return () => …)

untrack()#

read signals without subscribing

linked()#

writable derived that resets when its source changes; persisted() — a signal in localStorage/sessionStorage with cross-tab sync; selector() — O(2) updates for "selected row"; until() — await a signal (until(ready), until(n).toBe(3), { timeout })

provide() / inject()#

context down the scope chain and across islands via DOM ancestors

watch()#

onCleanup, { once }, pause() / resume(); computed((prev) => …, { initial })

Batch#

group updates, single flush

Reactive objects#

deep Proxy-based reactivity (like Vue reactive()), arrays included: push/splice/sort, index writes, length, key add/delete

Store#

definition-based reactive container (properties → signals, getters → computeds, methods → actions, auto-batched)

Interop#

from() turns an EventTarget, a producer or any { subscribe } (Preact, RxJS, Svelte store) into a signal; history() gives undo/redo for a signal, reactive() or store()

reactive() 2.0#

getters become computeds, methods become batched actions, $patch / $subscribe / $snapshot / $reset; only plain objects and arrays are wrapped (Date/Map/File stay as they are); store() = reactive(obj, { shallow: true })

watch()#

explicit dependency watching with debounce

Semantics#

A computed() is subscribed to its sources only while something observes it and needs no scope; what an effect creates in its body dies before the effect re-runs (Solid / Svelte 5 semantics); the setup ctx helpers survive an await. An exception inside computed() is a cached value, not a torn graph: subscriptions survive and the graph recovers as soon as a dependency changes; effect errors go to scope.onError → onError(fn) → reportError, never to the code that wrote the signal.

Edit this page on GitHub