Skip to content

Vanilla JavaScript

Mount a streamed GistUI program into any element with @gistui/vanilla - no framework, or any framework GistUI has no package for.

Updated View as Markdown

@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/catalog

Render 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).

Navigation

Type to search…

↑↓ navigate↵ selectEsc close