An Open Tech Foundation project
A tiny functional JavaScript UI library for AI agents to generate lightweight, interactive micro-apps.
- Tiny & functional — ~400 lines, no dependencies, no VDOM, no signals, no compiler.
- Native Custom Elements —
define(tag, setup)creates real Web Components, composable as<x-parent><x-child></x-parent>. - Closure state — ordinary
letvariables, no stores or reactive primitives. - Explicit updates —
update(el)batched viaqueueMicrotask, full control over re-render. - DOM identity — inputs, videos, canvases survive updates — no
innerHTMLrebuilds. - Keyed reconciliation —
key=${id}for stable lists (cart, data), positional fallback. - Attribute interpolation —
src=${url},style="background:${color}",class="btn ${active}". - AI-agent friendly —
define/html/update+mount— easy for agents to generate micro-apps without toolchain.
pnpm add @opentf/micro-ui
# or
npm i @opentf/micro-uiimport { define, html, update } from "@opentf/micro-ui";<script type="module">
import { define, html, update } from "@opentf/micro-ui";
define("x-counter", (el, props) => {
let count = Number(props.count || 0);
return () => html`
<button onclick=${() => { count++; update(el); }}>
Count: ${count}
</button>
`;
});
</script>
<x-counter count="0"></x-counter>Registers a custom element. setup(el, props) is called once on connect and must return a render function.
define("x-greeting", (el, props) => {
let name = props.name || "World";
return () => html`
<h2>Hello, ${name}</h2>
<input value=${name} oninput=${(e) => { name = e.target.value; update(el); }}>
`;
});Props are derived from the element's HTML attributes (strings) and stay reactive — parent htmlupdates child via explicitupdate+patchAttrs` sync.
Tagged template that produces an internal tree. Supports:
- Text interpolation:
<p>${value}</p> - Mixed content:
<p>Hello ${name}!</p> - Attribute interpolation:
<img src=${url}>,style="background:${color}",class="btn ${active}"(prefix/suffix + multiple interpolations) - Event binding:
<button onclick=${handler}>(viaon*) - Keyed lists:
<li key=${id}>for stable reorder / remove - Conditional:
${show ? htmlYes: null} - Lists:
${items.map(i => html - ${i.name}
- Nested html:
${childTemplate}
)}
Triggers a re-render (batched via queueMicrotask). The render function is called again, producing a new tree. The library reconciles old vs new, reusing DOM nodes wherever the structure matches. Controlled value/checked are synced as properties, not just attributes.
- State: ordinary JavaScript closures. No signals, stores, or reactive primitives.
- Composition: native Custom Elements.
<x-parent>contains<x-child>as regular HTML. - DOM identity: elements, inputs, videos, canvases survive updates. No
innerHTMLrebuilds. - Reconciliation: positional by default; keyed when
keypresent (key=${id}) — stable DOM reuse for cart / data lists. Matches old and new children by index or key. Unchanged nodes are reused. Changed nodes are patched in place.
Serve the project root with any static server (ES modules require it):
npx serve .
# or
python3 -m http.serverThen open test.html or demo.html.
- Not a React/Vue/Angular replacement for large apps
- Not optimized (v0 uses a full internal tree for correctness)
- Not a build tool or compiler
- No SSR/hydration — client islands only; updates are batched via
queueMicrotask
MIT