Skip to main content


📨 @lirx/core helps you to build and compose asynchronous and event-based programs. Everything being asynchronous, like dynamic variables, user events, http requests, data streams, is supported out from the box.

⚡️ It is extremely fast and small. Tree-shaking is fully supported, and the library is sufficiently optimized, so you can embed it in any applications, even the smallest ones.

🦸 This is your best ally when developing a web-app. Code in a more predictable way with clear data workflows, avoiding bugs, errors and inconsistent states.

Starting to think in Reactive Programming and using @lirx/core, will open so many opportunities, and simplify so much your work, that once started, you won't be able to do without it. They are revolutionary like Promises on their time.

First thing first, this library contains two complementary approach of Reactive Programming: Signals and Observables.

Signals provide a nice bridge between Imperative Programming and Reactive Programming, while Observables permit more complex data flows.


Signals are a perfect start as they are relatively easy to understand and straightforward to use.

They are similar to variables, with the specificity to be observable: if their value change, we may observe this change and react to it.

Let's illustrate it with a quick example:

// create a writable signal
const counter = signal(0);

// create a computed signal
const isEven = computed(() => counter() % 2 === 0);

// log the values of our signals, and log them again when either (or all) changes
effect(() => {
console.log('counter', counter(), 'isEven', isEven());

// create an observable triggered on a click on the window
fromEventTarget(window, 'click')(() => {
// update signal's value based on the current one
counter.update((currentValue) => currentValue + 1);

Signal's values are readable by calling the signal like a function (counter()), this is the imperative side of them. However, when using in contexts like effect or computed, we listen to their current state and future changes, thus, allowing us to create signals based on others (computed), or trigger a function when a change is detected (effect).

This capacity opens the gates to new designs, and elegant pipelines, where your variables contains always fresh data.


The Observables are the root of Reactive Programming: they are streams of values for which we create the pipeline first, and then, subscribe to them when requiring their values. They nature is asynchronous by default, and we will observe their changes:

// creates an Observable from a 'click' event on `document`, then pipe it
const subscribe = pipe$$(fromEventTarget(document, 'click'), [
// debounces the events: maximum one event every 1000ms
// like Array.reduce, but for Observables: here we increment count
scan$$$(count => count + 1, 0),
// we end up with an Observable, emitting the number of time
// we clicked on the document, with a minium interval of 1s

// finally we subscribe to the observable to log the value
subscribe(count => console.log(`clicked ${count} times`));

Observables are harder to master as they ask you to think in a totally different paradigm. However, they give you powerful tools to create complex data pipelines.

Usually, if you have an async data source (Events, Promises, HTTP Request, etc.), then, Observables are probably the right answer.

Where to start ?

First, if you want to go deeper on the concept of Reactive Programming, we wrote a gentle introduction to this paradigm.

However, if you want to go straight to the code, we recommend you to start from the Signals to get familiar with the primitives, and then go deeper with the Observables. We suggest you to go steps by steps, by starting to implement some signals in your application, and when you'll start to adopt the concept, add Observables to it.

Begin with the most essential functions, and when you'll be comfortable, start to implement more complex pipelines. After a while, you'll be so satisfied that you'll ask yourself: "How did I do without Observables?"