Counter
The counter is the “hello world” of UI frameworks. In Whisq, it’s 12 lines of code.
What You’ll Build
Section titled “What You’ll Build”A counter with increment, decrement, and reset buttons. The count updates reactively — no manual DOM manipulation.
Full Source
Section titled “Full Source”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")!);How It Works
Section titled “How It Works”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.
Key Concepts
Section titled “Key Concepts”- signal() — reactive state that triggers updates when written to
- Element functions —
div(),button(),span()— typed functions for every HTML element - Reactive children — pass a function
() => valueto make text update automatically - component() — wraps a function that returns UI, accepts props