Skip to content

Elements

In Whisq, every HTML element is a function. No JSX, no templates — just function calls with full TypeScript support.

import { div, h1, p, button, span } from "@whisq/core";
// With props + children
div({ class: "card", id: "main" },
h1("Title"),
p("Content"),
)
// Without props — just children
div(
h1("Title"),
p("Content"),
)
// Single text child
h1("Hello Whisq")
button("Click me")

The first argument is either a props object or a child. Whisq detects which automatically.

Every standard HTML tag has a function:

Layout: div, span, main, section, article, aside, header, footer, nav

Text: h1h6, p, strong, em, small, pre, code

Interactive: button, a

Forms: form, input, textarea, select, option, label

Lists: ul, ol, li

Table: table, thead, tbody, tr, th, td

Media: img, video, audio

Misc: br, hr, iframe

Pass a function for any prop to make it reactive:

import { signal, div, span } from "@whisq/core";
const active = signal(false);
// Reactive class
div({ class: () => active.value ? "card active" : "card" },
span("Content"),
)
// Reactive style
div({ style: () => `color: ${color.value}` }, "Styled text")
// Reactive visibility
div({ hidden: () => !visible.value }, "Now you see me")

Events use on* props with function handlers:

button({ onclick: () => count.value++ }, "Click me")
input({ oninput: (e) => name.value = e.target.value })
form({ onsubmit: (e) => { e.preventDefault(); save(); } },
// form children
)

All standard DOM events are supported: onclick, oninput, onchange, onsubmit, onkeydown, onmouseenter, onfocus, onblur, etc.

Pass a function as a child to make it reactive:

const name = signal("World");
div(
// Static child — never changes
h1("Hello"),
// Reactive child — updates when `name` changes
p(() => `Welcome, ${name.value}!`),
)

For dynamic tag names or custom elements:

import { h } from "@whisq/core";
h("custom-element", { class: "foo" }, "content")
h("details", { open: true }, h("summary", {}, "Click"), "Hidden content")

Access the underlying DOM element via the ref prop:

import { signal } from "@whisq/core";
const inputRef = signal<HTMLInputElement | null>(null);
input({ ref: inputRef, type: "text" })
// Later: inputRef.value?.focus()

Or with a callback:

input({
ref: (el) => el?.focus(),
type: "text",
})

Mount a WhisqNode into a DOM container:

import { div, mount } from "@whisq/core";
const dispose = mount(
div("Hello Whisq"),
document.getElementById("app")!,
);
// Later: dispose() to unmount and clean up
  • Components — Encapsulate elements in reusable components
  • Styling — Scoped CSS with sheet() and styles()