LLM Reference
This page contains the Whisq API in a format optimized for AI context windows. Copy and paste it into any AI assistant to get accurate Whisq code generation.
How to Use
Section titled “How to Use”- Pick a tier below based on your model’s free context.
- Copy that block (use the copy button in the top-right).
- Paste it at the start of your AI conversation.
- Describe the component you want to build.
This works with any LLM — ChatGPT, Claude, Gemini, Llama, Mistral, or local models.
Which tier?
Section titled “Which tier?”- Minimum (≤2 K tokens) —
signal,computed,effect,batch, elements, events, conditional and list rendering, components, basic async data, store pattern, anti-patterns. Pick this for small-context models (4 K / 8 K windows) or when the conversation already fills most of the window. - Complete (≤5 K tokens) — everything in Minimum plus class composition (
cx,rcx), scoped CSS and design tokens (sheet,styles,theme), context (createContext/provide/inject), error boundaries, keyed lists, andresource().refetch(). Pick this for frontier models or whenever you have headroom.
If you’re not sure, paste the Complete block — it’s still well under any modern model’s context window.
Minimum
Section titled “Minimum”# Whisq — AI-Native JavaScript Framework
## Importsimport { signal, computed, effect, batch, div, span, h1, h2, h3, p, button, input, textarea, select, option, a, img, ul, ol, li, table, thead, tbody, tr, th, td, form, label, header, footer, nav, main, section, article, aside, strong, em, h, raw, when, each, mount, component, onMount, onCleanup, resource,} from "@whisq/core";
## Reactive Stateconst count = signal(0); // create reactive valuecount.value; // read (triggers tracking)count.value = 5; // write (triggers updates)count.update(n => n + 1); // update via functioncount.peek(); // read WITHOUT trackingconst double = computed(() => count.value * 2); // derived valueeffect(() => console.log(count.value)); // side effectbatch(() => { x.value = 1; y.value = 2; }); // batch updates
## Elementsdiv({ class: "card" }, h1("Title"), p("Content")) // with propsdiv(h1("Title"), p("Content")) // without propsh1("Hello") // single child
## Reactive Propsdiv({ class: () => active.value ? "on" : "off" })div({ style: () => `color: ${c.value}` })div({ hidden: () => !visible.value })span(() => `Count: ${count.value}`) // reactive text child
## Eventsbutton({ onclick: () => count.value++ }, "Click")input({ oninput: (e) => name.value = e.target.value })form({ onsubmit: (e) => { e.preventDefault(); save(); } })
## Conditional Renderingwhen(() => loggedIn.value, () => p("Welcome!"), () => button({ onclick: login }, "Sign In"),)
## List Renderingul(each(() => items.value, (item) => li(item.name)))
## Componentsconst Counter = component((props: { initial?: number }) => { const count = signal(props.initial ?? 0); onMount(() => { console.log("mounted"); }); return div( button({ onclick: () => count.value-- }, "-"), span(() => count.value), button({ onclick: () => count.value++ }, "+"), );});mount(Counter({ initial: 10 }), document.getElementById("app"));
## Async Dataconst users = resource(() => fetch("/api/users").then(r => r.json()));when(() => users.loading(), () => p("Loading..."))when(() => !!users.error(), () => p("Error"))when(() => !!users.data(), () => ul(each(() => users.data(), u => li(u.name))))
## Shared State (Store Pattern)// stores/cart.tsexport const items = signal([]);export const total = computed(() => items.value.reduce((s, i) => s + i.price, 0));export const addItem = (item) => { items.value = [...items.value, item]; };
## Anti-Patterns// ❌ count.value as child — always wrap: () => count.value// ❌ items.value.push(x) — use: items.value = [...items.value, x]// ❌ JSX syntax — use hyperscript: div(), button(), span()// ❌ Class components — use component() function// ❌ this keyword — there is no this in WhisqComplete
Section titled “Complete”# Whisq — AI-Native JavaScript Framework
## Importsimport { signal, computed, effect, batch, div, span, h1, h2, h3, p, button, input, textarea, select, option, a, img, ul, ol, li, table, thead, tbody, tr, th, td, form, label, header, footer, nav, main, section, article, aside, strong, em, h, raw, when, each, mount, component, onMount, onCleanup, resource,} from "@whisq/core";
## Reactive Stateconst count = signal(0); // create reactive valuecount.value; // read (triggers tracking)count.value = 5; // write (triggers updates)count.update(n => n + 1); // update via functioncount.peek(); // read WITHOUT trackingconst double = computed(() => count.value * 2); // derived valueeffect(() => console.log(count.value)); // side effectbatch(() => { x.value = 1; y.value = 2; }); // batch updates
## Elementsdiv({ class: "card" }, h1("Title"), p("Content")) // with propsdiv(h1("Title"), p("Content")) // without propsh1("Hello") // single child
## Reactive Propsdiv({ class: () => active.value ? "on" : "off" })div({ style: () => `color: ${c.value}` })div({ hidden: () => !visible.value })span(() => `Count: ${count.value}`) // reactive text child
## Eventsbutton({ onclick: () => count.value++ }, "Click")input({ oninput: (e) => name.value = e.target.value })form({ onsubmit: (e) => { e.preventDefault(); save(); } })
## Conditional Renderingwhen(() => loggedIn.value, () => p("Welcome!"), () => button({ onclick: login }, "Sign In"),)
## List Renderingul(each(() => items.value, (item) => li(item.name)))
## Componentsconst Counter = component((props: { initial?: number }) => { const count = signal(props.initial ?? 0); onMount(() => { console.log("mounted"); }); return div( button({ onclick: () => count.value-- }, "-"), span(() => count.value), button({ onclick: () => count.value++ }, "+"), );});mount(Counter({ initial: 10 }), document.getElementById("app"));
## Async Dataconst users = resource(() => fetch("/api/users").then(r => r.json()));when(() => users.loading(), () => p("Loading..."))when(() => !!users.error(), () => p("Error"))when(() => !!users.data(), () => ul(each(() => users.data(), u => li(u.name))))
## Shared State (Store Pattern)// stores/cart.tsexport const items = signal([]);export const total = computed(() => items.value.reduce((s, i) => s + i.price, 0));export const addItem = (item) => { items.value = [...items.value, item]; };
## Anti-Patterns// ❌ count.value as child — always wrap: () => count.value// ❌ items.value.push(x) — use: items.value = [...items.value, x]// ❌ JSX syntax — use hyperscript: div(), button(), span()// ❌ Class components — use component() function// ❌ this keyword — there is no this in Whisq
// The Complete tier merges these imports with the ones from the minimum// block above. TypeScript and any reasonable bundler combine multiple// `import { ... } from "@whisq/core"` statements into a single binding.// Reactive collections live at a sub-path — import separately.import { cx, rcx, sheet, styles, theme, sx, createContext, provide, inject, errorBoundary, bind, match, ref, portal, transition, useHead,} from "@whisq/core";import { signalMap, signalSet } from "@whisq/core/collections";
## Class Namesconst isPrimary = true;div({ class: cx("btn", isPrimary && "btn-primary", { active: true }) }) // static// Use rcx when a class depends on a signal — pass a reactive getter:const loading = signal(false);div({ class: rcx("btn", () => loading.value && "btn-loading") })
## Styling (scoped CSS + tokens)theme({ color: { primary: "#4386FB" }, space: { md: "1rem" } }); // :root CSS varsconst s = sheet({ card: { padding: "var(--space-md)", background: "var(--color-primary)", "&:hover": { opacity: 0.9 } },});div({ class: s.card }, "Hello")// Reactive inline styles:const dark = signal(false);div({ style: styles({ color: () => dark.value ? "#fff" : "#111" }) })
## Context (no prop drilling)const ThemeCtx = createContext("light");const Parent = component(() => { provide(ThemeCtx, "dark"); return div(Child({}));});const Child = component(() => { const theme = inject(ThemeCtx); // "dark" return p(`Theme: ${theme}`);});
## Error BoundarieserrorBoundary( (error, retry) => { console.error("[whisq]", error); // or your reporter of choice return div( p(`Something went wrong: ${error.message}`), button({ onclick: retry }, "Retry"), ); }, () => RiskyComponent({}),)// Fallback receives (error, retry). retry re-runs the wrapped child.// Prefer over DIY try/catch in a component — that only catches sync setup.
## Two-way Binding (bind)// Spread bind(signal) into inputs — no manual value/oninput pairs.const name = signal("");input({ ...bind(name) }) // text / textarea / selectconst age = signal(0);input({ type: "number", ...bind(age, { as: "number" }) })const agreed = signal(false);input({ type: "checkbox", ...bind(agreed, { as: "checkbox" }) })const role = signal<"admin" | "user">("user");input({ type: "radio", ...bind(role, { as: "radio", value: "admin" }) })
## Tri-state Rendering (match)// First-true-wins. Trailing bare fn = fallback. Prefer over chained when()s// once you have 3+ branches — upgrades the minimum's Async Data pattern// with a retry affordance and an empty-state fallback.const users = resource(() => fetch("/api/users").then((r) => r.json()));div( match( [() => users.loading(), () => p("Loading…")], [() => !!users.error(), () => div( p(() => `Error: ${users.error()!.message}`), button({ onclick: () => users.refetch() }, "Retry"), )], [() => !!users.data(), () => ul(each(() => users.data()!, (u) => li(u.name)))], () => p("No data yet."), // fallback ),)
## DOM Refs (ref)// Signal that's populated with the element after mount, reset to null on unmount.const inputEl = ref<HTMLInputElement>();input({ ref: inputEl });onMount(() => inputEl.value?.focus());
## Reactive Collections (signalMap / signalSet)// Per-key / per-value subscriptions — effects re-run only when the specific// key or value they read changes (not on every structural change).const users = signalMap<string, { name: string }>();users.set("u1", { name: "Alice" });effect(() => console.log(users.get("u1")?.name)); // tracks "u1" onlyconst selected = signalSet<string>();selected.add("admin");effect(() => console.log(selected.has("admin"))); // tracks "admin" only
## Composable Inline Styles (sx)// sx() merges at component setup — truthy sources are kept, falsy skipped.// For reactive values, pass a FUNCTION per property (the style prop's// per-property reactivity sees them). A bare `signal.value && {...}`// source is frozen at setup; use a function-valued prop instead.const active = signal(false);const x = signal(0);div({ style: sx( { color: "red", padding: "1rem" }, // base { borderColor: () => active.value ? "blue" : "transparent" }, // reactive conditional { transform: () => `translateX(${x.value}px)` }, // reactive ),})
## Portals// Render into a different DOM target — escapes parent overflow / z-index.portal(document.body, div({ class: "modal-overlay" }, "Hello"))
## Transitions// CSS keyframe enter/exit. Each prop entry is [from, to]; duration/easing// apply to that phase.transition( div({ class: "toast" }, "Saved"), { enter: { opacity: [0, 1], transform: ["translateY(10px)", "translateY(0)"], duration: 200 }, exit: { opacity: [1, 0], duration: 150 }, },)
## Head Management (useHead)// Reactive title / meta / link. Call inside component() setup;// the tags are auto-removed when the component unmounts.useHead({ title: () => `${page.value} — Whisq`, meta: [{ name: "description", content: () => desc.value }], link: [{ rel: "stylesheet", href: "/style.css" }],})
## Keyed Lists// LIS-based diffing — only changed nodes move/insert/removeul(each(() => todos.value, (t) => li(t.text), { key: (t) => t.id }))
## Async Data — Refetchbutton({ onclick: () => users.refetch() }, "Retry") // re-run the fetcherWhy this works
Section titled “Why this works”The reference above stays within its advertised tier budgets — under 2 K tokens for Minimum and under 5 K for Complete — so it fits any model’s context window with room for a real conversation. It covers:
- All reactive primitives (signal, computed, effect, batch)
- All element functions
- Event handling patterns
- Conditional and list rendering (with keyed reconciliation in Complete)
- Components with lifecycle
- Class-name composition (Complete: cx, rcx)
- Scoped CSS and design tokens (Complete: sheet, styles, theme)
- Context for prop-drill-free state (Complete: createContext, provide, inject)
- Error boundaries (Complete)
- Async data fetching (with refetch in Complete)
- Shared state pattern
- Common anti-patterns
Because Whisq uses plain JavaScript functions with no special syntax, AI models generate correct code with high accuracy. There’s no JSX to misformat, no hook ordering to violate, and no template DSL to hallucinate.
Next Steps
Section titled “Next Steps”- Claude Code — Set up for Claude Code with CLAUDE.md
- Cursor — Set up .cursorrules for Cursor
- MCP Server — AI tooling with component scaffolding