Skip to content

Repository files navigation

signal

GitHub | NPM | API Doc

Small, framework-independent signals for TypeScript.

signal provides a small reactive core with explicit ownership, lazy memos, synchronous effects and async resources. Its API is heavily inspired by SolidJS while staying usable as a standalone library in browsers and Node.js, without framework lifecycles.

Observable interop is available through @kayahr/observable.

Scope management and ownership boundaries are provided by @kayahr/scope.

Features

  • Writable signals with configurable equality
  • Lazy memos with dynamic dependency tracking
  • Synchronous effects with cleanup and previous-value support
  • Optional ownership scopes
  • Synchronous batching and explicit untrack
  • Async resources with loading, error, reload and disposal control
  • Array signals without deep array comparisons
  • Observable interop through toSignal, toObservable and toSubscriber

Installation

npm install @kayahr/signal

Examples

Use signals directly when no explicit ownership boundary is needed:

import { createEffect, createMemo, createSignal } from "@kayahr/signal";

const [ count, setCount ] = createSignal(0);
const doubled = createMemo(() => count() * 2);

createEffect(() => {
    console.log(`count=${count()} doubled=${doubled()}`);
});

setCount(1);
setCount(previous => previous + 1);

Use a scope when you want explicit ownership and later cleanup for a whole reactive subgraph:

import { createScope } from "@kayahr/scope";
import { ResourceStatus, createEffect, createMemo, createResource, createSignal } from "@kayahr/signal";

const scope = createScope();
const { setCount, setUserId } = scope.run(() => {
    const [ count, setCount ] = createSignal(0);
    const doubled = createMemo(() => count() * 2);
    const [ userId, setUserId ] = createSignal(1);
    const [ user, resource ] = createResource(userId, async (id, abortSignal) => {
        const response = await fetch(`/api/users/${id}`, { signal: abortSignal });
        return response.json() as Promise<{ name: string }>;
    });

    createEffect(() => {
        console.log(`count=${count()} doubled=${doubled()}`);
        if (resource.status() === ResourceStatus.Ready) {
            console.log(user()?.name);
        }
    });

    return { setCount, setUserId };
});

setCount(1);
setCount(previous => previous + 1);
setUserId(2);

// ...
scope.dispose();

Documentation

About

Standalone signals library for browser and Node.js

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Sponsor this project

Used by

Contributors

Languages