@gistui/vanilla mounts a program into any element, with the same markup, styles and behaviour as @gistui/react. It is the base of the Vue, Svelte and Solid packages, and it is what to use in Angular, Astro, Web Components, a plain HTML page, or anything else.
npm i @gistui/vanilla @gistui/styles @gistui/catalogyarn add @gistui/vanilla @gistui/styles @gistui/catalogpnpm add @gistui/vanilla @gistui/styles @gistui/catalogbun add @gistui/vanilla @gistui/styles @gistui/catalogRender an answer
import "@gistui/styles/styles.css";
import { mount } from "@gistui/vanilla";
import { ui } from "@gistui/vanilla/ui";
const view = mount(document.getElementById("answer")!, {
library: ui,
stream: response.body, // a ReadableStream or async iterable of text or bytes
onAction: (action) => console.log(action),
});Or feed it a growing string. Only the appended part is parsed.
view.update({ source: textSoFar, streaming: true });
view.update({ source: fullText, streaming: false });
view.destroy();view.update() takes any subset of the options, so it is also how you change the theme or the tools later.
Options
stream or source + streaming, inline, onAction, onError, onStateChange, onProse, theme ("system" | "light" | "dark"), color, tokens, darkTokens, classNames, tools, mutations, onToolCall, allowedHosts, paused, initialState, lockUntil ("done" or "ready"), openLinks, autofix, onAutofix.
They mean the same as in every renderer: see Renderer options.
Tools and safety
A program is untrusted: a model wrote it, possibly steered by content it read. It cannot run code.
| Option | What it does |
|---|---|
tools |
Read-only functions (or an MCP-style client, { callTool }). @query may call them as soon as the UI renders, so nothing here may change anything. |
mutations |
Functions that change something. They run only when a person presses a button. |
onToolCall |
Called before every tool call with { name, args, kind, nodeId }. Return false to block it. |
allowedHosts |
Hosts that images, video and backgrounds may load from, for example ["cdn.example.com", "*.example.com"]. Set it when the model sees private data. |
paused |
Stops timed data refreshes. |
More in Security and Tools and live data.
Your own components
Replace a built-in component and keep its name, or add new ones.
import { h } from "@gistui/vanilla";
import { ui } from "@gistui/vanilla/ui";
const lib = ui.extend({
Card: (ctx) => {
const el = h("section", { class: "my-card" });
ctx.place(el); // the card's children go here
return { el, update: () => true };
},
});A renderer gets ctx (props, node, place, binding(), emit, run, locked, streaming…) and returns { el, update?, destroy? }. A form field of your own joins the Form around it with formField(ctx, onChange).