Signals

A signal is the unit of reactive state. signal returns one binding: call it to read, .set() to write.

import { signal } from '@fluixi/reactive/signal';

const count = signal(0);

count();               // read  → 0
count.set(1);          // write → 1
count.set(c => c + 1); // update from the previous value → 2

Inside a component, $signal is the same thing without the import, the compiler resolves it and adds what it needs:

const count = $signal(0);

Reading is tracking

Calling the signal inside a memo or effect subscribes that computation to the signal. The next write re-runs only the computations that read it:

effect(() => {
  console.log('count is', count()); // subscribes to count
});
count.set(5); // logs "count is 5"

Reading outside any reactive scope just returns the value, no subscription.

Equality

By default a signal skips notifying readers when the new value is === the old one. Pass equals to customize, or equals: false to always notify:

const list = signal([], { equals: false });

Reading without subscribing

Use untrack to read a signal without creating a dependency:

import { untrack } from '@fluixi/reactive/signal';

effect(() => {
  draw(count(), untrack(theme)); // re-runs on count, but not on theme
});

$untrack is the same without the import.

The tuple form

createSignal returns the same signal as a [read, write] pair:

import { createSignal } from '@fluixi/reactive/signal';

const [count, setCount] = createSignal(0);

count();       // read
setCount(1);   // write

These are one primitive with two spellings, not two implementations, signal is createSignal with the pair packed into an object, and its get/set are that pair. So they track each other, mix freely in a module, and behave identically through SSR and hydration. Prefer signal; reach for the tuple when you want the halves apart, and note that plenty of existing code and libraries are written against it.

Intrinsics

In an app built by @fluixi/start, the $ forms need no import: the compiler rewrites each to the runtime call and adds the import. They are not a second reactive system, $signal compiles to signal. A local binding of the same name wins, and the compiler says so.

Intrinsic Stands for Imported from
$signal, $memo, $effect signal, memo, effect @fluixi/reactive/signal
$resource resource @fluixi/reactive/signal
$cachedResource cachedResource @fluixi/reactive/cached-resource
$store, $untrackStore store, untrackStore @fluixi/reactive/store
$untrack, $batch, $root, $context untrack, batch, root, context @fluixi/reactive/signal
$cleanup onCleanup @fluixi/reactive/signal
$renderEffect, $computed renderEffect, computed @fluixi/reactive/signal
$selector, $deferred createSelector, createDeferred @fluixi/reactive/signal
$storage, $asyncStorage, $persisted useStorage, useAsyncStorage, persisted @fluixi/core/storage
$env env @fluixi/start/env

Package code, which the start compiler does not process, imports the runtime names.

Next: Derived values.

Released under the MIT License.

Copyright © 2026 Ibrahima Touré