npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

trkl

v2.0.1

Published

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

Downloads

125

Readme

Logo

trkl

Reactive JavaScript programming in less than half a kilobyte.

For just a meagre 383 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
  • TypeScript project support

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

Data is held in Observables, which expose pub / sub capabilities:

const apples = trkl(2);

apples.subscribe(n => console.log(`There are ${n} apples`));

apples(7); // Prints "There are 7 apples"

Combine data from observables using Computeds, which automatically track their dependencies:

const bananas = trkl(3);

const fruit = trkl.computed(()=> {
    return apples() + bananas();
});

fruit.subscribe(n => console.log(`There are ${n} fruit in total`));

apples(6); // Prints "There are 9 fruit in total"

In a nutshell: Trkl lets you create observable channels of data, and then combine them with functions to result in further observables.

Installation

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.

Versions

If you need AMD support, use v1.5.1

v2+ is ES6 only; v1.x supports ES5

Importing

Node / SystemJS: const trkl = require('trkl');

ES6 / TypeScript: import * as trkl from 'trkl';

TypeScript support

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);

API

trkl()

Creates an observable with optional supplied initial value.

let observable = trkl('foo');

observable()

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

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

trkl.computed(fn)

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 => {
    print(`a + b = ${newVal}`);
});

a(5); // Print "a + b = 5"
b(3); // Print "a + b = 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!

const a = trkl('A');
const b = trkl('B');
const readA = trkl(true);

const reader = trkl.computed(()=> {
    return readA() ? a() : b();
});

print(reader()); // 'A'

readA(false);

print(reader()); // '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:

Error: Circular computation

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};
        observable(coordinates);
    });
});

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 safely mutate the observable's subscriber list (e.g. a subscriber can remove itself)

How updates are deduplicated

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)) {
      observer(newVal);
    } else if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
      observer(newVal);
    }
  });
});

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

observable.unsubscribe(fn)

Remove the specified function as a subscriber.

React hooks example

You can easily make a stateful hook out of a Trkl observable. Use this to create a 'global' state hook:

import { useState, useEffect } from 'react';

const greeting = trkl('hello');

export function useGreeting() {
  const [ state, setState ] = useState(greeting());

  useEffect(() => {
    greeting.subscribe(setState)

    return () => greeting.unsubscribe(setState); // works fine as the setState function is stable between renders :-)
  }, []);

  return [ state, greeting ];
}

Why 'trkl'?

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