REFERENCE / CORE API

signal()

Creates a signal: a value that remembers who read it, and wakes exactly those readers when it changes — never a component, never a tree.

Signature

TS
signal<T>(initial: T): Signal<T>

Parameters

PARAMETERTYPEDESCRIPTION
initial T The starting value. Any type. A signal never looks inside its value: replacing it is the only write it sees, so an array or object mutated in place notifies nobody — assign a new one.

Returns

Signal<T> — read and write through .value; read without subscribing through .peek(). A write that leaves the value unchanged (Object.is) is a no-op: nothing downstream runs.

Reading and writing

JS
const count = signal(0);

count.value;        // 0 — reading inside a computed, effect or template subscribes
count.value = 1;    // notifies exactly the readers of this signal
count.value = 1;    // same value: nobody is notified (Object.is)
count.peek();       // 1 — reads without subscribing

In templates

Inside html``, hand over the signal itself, not .value. The template binds exactly one text node (or one attribute) to it, and that node is the only thing updated on a write. Reading .value there bakes the current value in as a constant.

JS
const name = signal('world');

html`<h1>hello, ${name}</h1>`;         // hand over the signal: ONE text node follows it
html`<h1>hello, ${name.value}</h1>`;   // a plain string: written once, never updates

Writes and the DOM

Writing .value updates the value immediately — the next line reads the new one. The effects that follow it, including every DOM binding, run once in a microtask after the current task, so a burst of writes costs one DOM pass. flushSync() runs the pending effects right now; await tick() waits for them.

JS
const a = signal(1);
const b = signal(2);

effect(() => console.log(a.value + b.value));   // logs 3 at creation

a.value = 10;   // the value is 10 right here —
b.value = 20;   // — but the effect runs once, in a microtask, after both writes: 30

flushSync();    // or run every pending effect now

Try it

src/islands/counter.ts
import { signal, html, mount } from '@amojs.dev/core';

const count = signal(0);

const Counter = () =>
  html`<button class="counter" onclick=${() => count.value++}>clicked ${count} times</button>`;

const host = document.getElementById('demo');
if (host) mount(Counter, host);
live preview● running

See also