Skip to content

API

TypeScript declarations ship with the package, so your editor has all of this too.

A readable and writable reactive value.

const count = signal(0);
count(); // read, and subscribe if inside a computation
count(5); // write
count.set(5); // write, explicitly
count.update((n) => n + 1); // write from the current value
count.peek(); // read without subscribing

equals defaults to ===. Pass a comparator to control when a write counts as a change.

A lazy, memoised derived value. Recomputes only when read and a source changed. If it lands on the same value, nothing downstream is woken.

const area = computed(() => width() * height());
area(); // read
area.peek(); // read without subscribing

Runs fn now, and again whenever a value it read changes. Returns a disposer. fn may return a cleanup, run before the next pass and on disposal.

const stop = effect(() => {
const id = setInterval(tick, delay());
return () => clearInterval(id);
});
stop();

Group writes so dependents run once at the end. Returns whatever fn returns.

Read without creating a dependency.

Run any queued effects immediately. Rarely needed — writes apply synchronously.

true while a subscriber (an effect or a computed) is collecting dependencies — reads made now will subscribe it. For store-like integrations that want to skip subscription bookkeeping on untracked reads; application code rarely needs it.

Create an ownership scope. Returns a disposer that tears down every effect created inside.

Register a teardown with the nearest enclosing effect or root.

A tagged template. Parsed once per call site. See template syntax.

The same, for fragments that belong inside an <svg>.

A list where each row gets its own reactive scope, created once.

each(
() => items(), // source
(item, index) => html`<li>${() => item().t}</li>`, // row; both are signals
(item) => item.id // identity, optional
)

index is only created if the render function declares a second parameter.

Give a hand-mapped list item a stable identity. Prefer each past a few dozen items.

Render outside a component. Returns a disposer that removes the DOM and stops every binding.

Cached CSS. Identical text returns the same constructed stylesheet, so it is parsed once for the whole page. Interpolating one sheet into another composes them.

const sheet = css`:host { display: block }`;
sheet.text; // the CSS text
sheet.sheet; // the CSSStyleSheet, or null if unsupported
sheet.replace(':host { … }'); // rewrite in place; every adopter updates

Declare the custom properties a component is themed by.

const t = vars('btn', { bg: '#111', borderRadius: '8px' });
t.bg; // 'var(--btn-bg, #111)'
t.names; // ['--btn-bg', '--btn-border-radius']
t.prefix; // 'btn'

camelCase keys become kebab-case properties.

Push styles into every component, including ones created later. Applied after each component’s own styles, so a theme wins ties. Returns a function that removes them.

Register a custom element.

define('x-thing', {
props: { label: '', count: 0 },
styles: css`:host { display: block }`,
shadow: 'open', // 'open' | 'closed' | false
formAssociated: false, // true: a form-associated custom element
setup(props, host) { return html``; },
});
define('x-simple', () => html`<p>hi</p>`);

Returns the element constructor.

Inside setup:

  • props.x — the prop as a signal (props.x() reads, props.x.set(v) writes)
  • host — the element, with host.props and host.emit
  • with formAssociated: true: host.internals — the element’s ElementInternals (setFormValue, setValidity, form, …) — and the form lifecycle reactions forwarded to handlers assigned in setup: host.onFormAssociated(form), host.onFormDisabled(disabled), host.onFormReset(), host.onFormStateRestore(state, mode). The initial association/disabled state precedes setup — read host.internals.form and host.matches(':disabled') for the starting state; the handlers hear changes from then on

On the element:

  • el.x — the same prop as a DOM property
  • x="…" — the matching attribute (camelCasekebab-case)
  • from a parent template, x=${signal} (or .x=${signal}) sets that property and stays live — objects and camelCase names included
  • el.emit(type, detail?, init?) — dispatch a bubbling, composed CustomEvent; returns false if cancelled

A deeply reactive object. Reading a path subscribes to that path; writing one wakes only those readers. Plain objects and arrays are made reactive recursively; everything else is returned as-is.

Array mutators (push, splice, sort, …) apply atomically.

The raw object, with no proxy and no tracking.

Apply many mutations as one update.

Read without subscribing.

Turn an O(n) “which one is selected?” test into an O(1) one. Returns a function that takes a key and returns a boolean, flipping only the two keys that changed.

const isSelected = selector(() => state.selected);
isSelected(row.id); // boolean

A context key. Identity is what matters — import the same object in the provider and the consumer.

Serve a value to descendants. value may be a plain value, a signal, or any function; reactive values are pushed to subscribers when they change. Returns a function that stops providing.

Ask the nearest provider above host. Returns a read-only signal, holding fallback until a provider answers.

provide, torn down automatically with the enclosing scope.

The reactive core with no DOM dependency: signal, computed, effect, batch, untrack, tracking, flush, root, onCleanup.

It is a separate build with its own graph. If you are rendering, import your signals from @alacris/core.