Sign Up for Free

RunKit +

Try any Node.js package right in your browser

This is a playground to test code. It runs a full Node.js environment and already has all of npm’s 400,000 packages pre-installed, including trkl with all npm packages installed. Try it out:

var trkl = require("trkl")

This service is provided by RunKit and is not affiliated with npm, Inc or the package authors.

trkl v1.9.2

Reactive microlibrary offering Knockout-style observables and computeds for less than 500 bytes


Reactive JavaScript programming in less than half a kilobyte. Supports TypeScript.

For just a meagre 434 bytes (minified and gzipped), you get

  • observables with a pub/sub interface
  • powerful Knockout.js-style computeds with proper "magical" dependency tracking
  • circular reference detection

The basic idea is to provide the most 'bang for buck' in terms of bytes down the wire versus expressiveness and utility.

My motto is: "If you can find a smaller reactive programming microlibrary... keep it to yourself"

Give me the gist

* Pub-sub
const oranges = trkl(4);

oranges.subscribe(_ => console.log('We have', _, 'oranges'));

oranges(5); // Console logs, "We have 5 oranges"

* Computeds

const apples = trkl(2);
const bananas = trkl(5);

trkl.computed(()=> {
    const totalFruit = apples() + bananas();
    console.log('We have', totalFruit, 'fruit');
// Console logs, "We have 7 fruit"

apples(4); // Console logs, "We have 9 fruit"


You can either drop trkl.min.js straight into your project, or run

npm install trkl --save

Trkl works in both CommonJS and browser environments. If you need AMD support, use v1.5.1

Use with TypeScript

Just import * as trkl from 'trkl'.

Types are defined in index.d.ts.

It's assumed that the types inside observables are immutable. If you need to initialise a type-less observable use foo = trkl(undefined as any);



Creates an observable with optional supplied initial value.

let observable = trkl('foo');


Call without arguments to get the value, call with an argument to set it.

observable();       // getter
observable('foo');  // setter


Creates an observable that executes a function which calls other observables, and re-runs that function whenever those dependencies change.

If you've used Knockout computeds, you'll know exactly how these work. Here's an example:

let a = trkl(0);
let b = trkl(0);

let c = trkl.computed(()=> {
    return a() + b();

c.subscribe(newVal => {
    console.log("c's value is now ", newVal);

a(5); // Console => "c's value is now 5"
b(3); // Console => "c's value is now 8"

You don't have to provide anything to computed to notify if it of your dependencies. This differs from other libraries, where you have to remember to explicitly pass in all the observables your computation depends on.

Dependencies can even be dynamic!

let a = trkl(1);
let b = trkl(2);
let bool = trkl(true);

const c = trkl.computed(()=> {
    if (bool()) {
        console.log('A is', a());
    } else {
        console.log('B is', b());

// Console log -> "A is 1"
// Console log -> "A is 3"
// Console log -> "B is 2"
// Console log -> "B is 4"
// Console log -> "A is 3"

The computed c starts with a dependency on bool and a. When bool changes, we re-run the function and capture a dependency on b.

What about circular references?

If we have an observable a that informs an computed b, and then we have a new computed c that takes the value of b and inserts it into a, we get a triangular flow of information.

Luckily, trkl will detect such instances and immediately throw an exception:

Circular reference detected

trkl.from(observable => {...})

Create an observable, and pass it to your supplied function. That function may then set up event handlers to change the observable's state.

For instance, to create an observable that tracks the x/y coordinates of user clicks:

const clicks = trkl.from(observable => {
    window.addEventListener('click', e => {
        const coordinates = {x: e.clientX, y: e.clientY};

clicks.subscribe(coordinates => console.log(coordinates));

Every time the user clicks, clicks is updated with the latest coordinates.

observable.subscribe(fn, ?immediate)

When an observable's value changes, pass its new and old values to the supplied subscriber.

let numbers = trkl(1);
numbers.subscribe((newVal, oldVal) => {
    console.log('The observable was changed from', oldVal, 'to', newVal);

numbers(2); // console outputs 'The observable was changed from 1 to 2'

If you pass the same subscriber multiple times, it will be de-duplicated, and only run once.

If you pass a truthy value to immediate, the subscriber will also run immediately.

A subscription can mutate the observable's subscriber list (e.g. a subscriber can remove itself), but the mutation won't take effect until the next time the observer changes.

A note on deduplication

Note that Trkl will only filter out duplicate updates if the values are primitives, not objects or arrays. Why? Well, if you have two objects or arrays, you can only tell if their values have changed by recursively inspecting the whole tree of their properties. This would be expensive, and could lead us into circular inspections, so for performance and size reasons we don't bother.

If you really need to filter out duplicates, you could always do

const filter = trkl.from(observer => {
  source.subscribe((newVal, oldVal) => {
    if (newVal.length && (newVal.length !== oldVal.length)) {
    } else if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {

This will only work if your objects / arrays are JSON-serializable, though.


Remove the specified function as a subscriber.

Why 'trkl'?

Because it's like a stream, except smaller (a 'trickle'), except even smaller than that ('trkl').

RunKit is a free, in-browser JavaScript dev environment for prototyping Node.js code, with every npm package installed. Sign up to share your code.
Sign Up for Free