Skip to content

How Signy works

Render once, then let signals update the exact nodes that read them.

Render once

A component's render() runs one time and returns DOM nodes. There is no virtual DOM and no second render. What changes later is wired up during that first render, as small effects bound to the exact text nodes and attributes that need them.

Automatic tracking

When a function bound in a template reads signal.value, the signal remembers it. A write to the signal runs only the functions that read it. You never list dependencies.

html`
  <p>${() => this.count.value}</p>     <!-- live: updates on every write -->
  <p>${this.count.value}</p>           <!-- frozen: the value at render time -->
`;

Updates in a microtask

Signal writes are collected and applied together in a microtask, so five writes in a row cause one DOM update. Use batch() to group writes explicitly, and flushAll() in tests to apply them at once.

Ownership

Everything a component creates while rendering belongs to it: bindings, effects created in setup(), control flow branches and child components. Unmounting the component disposes all of it.

Not React

Signy looks familiar, but some React habits do not apply.

  • There is no useState, useEffect or setState. State is a signal, side effects are effect().
  • Do not expect render() to run again. Bind a function where a value must update.
  • Do not .map() a signal array in a template. It renders once. Use For.
  • Use class and onClick, not className or onclick.