Skip to content

Counter

The counter is the “hello world” of UI frameworks. In Whisq, it’s 12 lines of code.

A counter with increment, decrement, and reset buttons. The count updates reactively — no manual DOM manipulation.

import { signal, component, div, button, span, mount } from "@whisq/core";
const Counter = component((props: { initial?: number }) => {
const count = signal(props.initial ?? 0);
return div({ class: "counter" },
button({ onclick: () => count.value-- }, "-"),
span({ class: "count" }, () => ` ${count.value} `),
button({ onclick: () => count.value++ }, "+"),
button({ onclick: () => count.value = 0 }, "Reset"),
);
});
mount(Counter({ initial: 0 }), document.getElementById("app")!);

1. Create reactive state

const count = signal(props.initial ?? 0);

signal(0) creates a reactive value. Reading count.value tracks the dependency. Writing to it triggers UI updates.

2. Build the UI with element functions

div({ class: "counter" },
button({ onclick: () => count.value-- }, "-"),
span({ class: "count" }, () => ` ${count.value} `),
button({ onclick: () => count.value++ }, "+"),
);

Each HTML element is a function. The first argument is an optional props object, the rest are children. The span child is a function () => count.value — this is what makes it reactive.

3. Mount to the DOM

mount(Counter({ initial: 0 }), document.getElementById("app")!);

mount() attaches the component tree to a DOM element.

  • signal() — reactive state that triggers updates when written to
  • Element functionsdiv(), button(), span() — typed functions for every HTML element
  • Reactive children — pass a function () => value to make text update automatically
  • component() — wraps a function that returns UI, accepts props
  • Todo App — A more complete example with lists and state
  • Signals — Deep dive into reactive state
  • Elements — All element functions and props