Core API
The whole framework is nine functions.
import {
html, mount, createState, computed, each, effect, boundary, configure, onCleanup
} from "@zoijs/core";| Function | Purpose |
|---|---|
html | Tagged-template renderer (real HTML, parsed once) |
mount(component, target) | Render a component; returns unmount() |
createState(value) | A reactive value — get / set / peek |
computed(fn) | A lazy, cached, value-gated derived value |
effect(fn) | Run a side effect when reads change; returns { dispose } |
boundary(child, fallback) | Render a fallback if a subtree throws while building |
each(items, keyFn, renderFn) | Keyed list rendering |
configure({ dev }) | Toggle development warnings |
onCleanup(fn) | Teardown for a component or list item |
html#
A tagged template that turns real HTML into live DOM. Interpolate text, attributes, and event handlers. Wrap a value in an arrow to make it reactive.
html`<button onclick=${onClick} class=${() => cls}>${() => label}</button>`mount#
Runs a component once, inserts its DOM into a target (element or selector), and returns an unmount() that disposes everything it created.
const unmount = mount(Counter, "#app");
// later…
unmount();createState#
A reactive value. Reading it inside a binding subscribes that binding; writing it updates only the dependents that actually changed.
const count = createState(0);
count.get(); // 0 (subscribes inside a binding)
count.set(5); // updates dependents
count.peek(); // read without subscribingcomputed#
A derived value that is lazy and cached. It recomputes only when a dependency changes, and won't wake the DOM if its result is unchanged.
const first = createState("Ada");
const last = createState("Lovelace");
const full = computed(() => first.get() + " " + last.get());
full.get(); // "Ada Lovelace" — cached, recomputed only when a source changeseffect#
Run a side effect that re-runs when a reactive value it reads changes — for work outside the view (persist, set the title, drive a non-Zoijs widget). For on-screen content, use a binding. It may return a cleanup (runs before each re-run and on dispose), returns { dispose }, and auto-disposes with its owner.
const theme = createState("light");
// runs now, and re-runs whenever `theme` changes
effect(() => localStorage.setItem("theme", theme.get()));
// return a cleanup — runs before each re-run and on dispose
effect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
});boundary#
Render a subtree; if it throws while building its markup (an error that would otherwise break the whole mount), tear down the partial work and render a fallback instead. For untrusted subtrees — third-party widgets, foreign data. Catches synchronous setup/render throws only.
html`<section>
${boundary(
() => RiskyWidget(),
(err) => html`<p>Couldn't load this.</p>`
)}
</section>`each#
Keyed list rendering. Reordering moves DOM nodes instead of rebuilding them, preserving focus, input values, and scroll position.
html`
<ul>
${each(
() => todos.get(), // items (reactive)
(t) => t.id, // stable key
(t) => html`<li>${() => t.text}</li>`
)}
</ul>
`configure#
Toggle development warnings. Leave them on in development; turn them off in production for a little less overhead.
configure({ dev: false });onCleanup#
Register a teardown function for the current component or list item. It runs when that component unmounts or that item is removed — perfect for timers and subscriptions.
function Clock() {
const now = createState(Date.now());
const id = setInterval(() => now.set(Date.now()), 1000);
onCleanup(() => clearInterval(id)); // runs on unmount
return html`<time>${() => now.get()}</time>`;
}