A lightweight, zero-dependency, and implicitly reactive signals engine for modern JavaScript applications. Built around native web standards, it mirrors the core mechanics of the upcoming TC39 Signals Proposal to provide high-performance, fine-grained UI synchronization without a mandatory compilation step.
Unlike explicit state containers that require string keys or manual handshakes, @blackbirdjs/signals handles dependency tracking automatically. When a tracker reads a value, the engine maps the link dynamically behind the scenes.
- Implicit Dependency Tracking: Forget string-based subscriptions. Simply read
.valueinside atrack()container, and the engine handles the mapping automatically. - Memory-Safe O(1) Cleanups: The
track()handle returns a discrete subscription object. Calling.unsubscribe()removes that specific tracker cleanly from memory, preventing ghost handlers and memory leaks during page transitions. - Identity Performance Guard: Automatically short-circuits and blocks redundant calculations if an incoming value matches the existing state primitive.
- 100% Isomorphic & Zero-Build: Fully compatible with browsers (via native script tags or CDNs), Node.js runtimes, and Edge workers without Babel, Vite, or special build configs.
npm install @blackbirdjs/signalsimport { signal, track } from '@blackbirdjs/signals';
// Declare atomic, reactive containers
const count = signal(0);
// Set up an automated tracking loop
// This container executes IMMEDIATELY, then watches any signal read within its block
const sub = track(() => {
console.log(`Live View Tracker: Count value is ${count.value}`);
});
// Instantly logs: "Live View Tracker: Count value is 0"
// Mutate values like normal variables
count.value = 1; // Logs: "Live View Tracker: Count value is 1"
count.value = 2; // Logs: "Live View Tracker: Count value is 2"
// The identity guard blocks redundant changes automatically
count.value = 2; // Short-circuits quietly. Zero processing overhead wasted.
// Clean up memory allocations when a view or component unmounts
sub.unsubscribe();
count.value = 3; // Completely quiet! This tracker has been safely dismantled.BlackbirdJS offers two fine-grained approaches to reactivity depending on your performance targets:
- Use
@blackbirdjs/storefor Global Architectures: Best for global data layers. It uses explicit string keys (store.get('user')) for absolute peak performance with zero read overhead. - Use
@blackbirdjs/signalsfor Fluid UI Wiring: Best for localized UI synchronization and auto-calculated models. It trades a fraction of execution speed for automatic tracking and modern, callback-free code layout styling.
Instantiates a unique atomic reactive container wrapping a primitive or structural data point.
Opens a temporary runtime frame to capture signals evaluated within the block. Automatically registers the wrapper container inside target nodes and returns a disposition object.
Returns:
Object: A subscription handle containing:unsubscribe():Function— Removes the tracking loop cleanly from all parent signals at constant timeO(1)complexity with no garbage collection overhead.
.value(Getter/Setter): Reads data and logs active tracking contexts, or writes data and propagates cascades to downstream listeners..subscriberCount(Getter): Returns a numeric evaluation of active tracking loops bound to the node..clearSubscribers(): Sweeps the private collectorSet, instantly dropping all connected trackers in a single action.
Licensed under the Apache-2.0 License.