GistUI has four moving parts: a language the model writes, a catalog that says which components exist, a processor that turns text into a tree, and a renderer that draws it.
1. The model writes statements
A GistUI program is a list of short statements, one per line: id = value. The first is root. Later lines fill in the parts it refers to.
root = Page(head, kpis)
head = Header("Q3 revenue", "All regions")
kpis = Stats(kpiData)
kpiData = |Label|Value|Delta
|Revenue|$1.2M|+8%
|Customers|3,410|+2.1%Three choices in the language make streaming work:
- Layout first, data last.
rootarrives in the first line, so the page has a shape before any data exists. Parts that are not defined yet show as placeholders. - Flat and line-based. A line is complete when its newline arrives. Nothing has to be closed far away, unlike nested JSON.
- Data written once. A table is pipe rows, and one table can feed a chart, a table and a KPI row.
2. The catalog directs the parser
The catalog lists every component with its positional arguments, props, enum values and flags. The parser reads with the catalog in hand, so short forms are never ambiguous:
card = Card(a, b, sunk, gap:lg)sunk is an enum value of Card’s v prop, gap:lg is a named prop, and a and b are references. The parser decides this from the catalog and what it has already read, never from text that has not arrived yet.
The same catalog generates the system prompt, so the model is told exactly what the parser accepts.
3. The processor emits patches
The parser is incremental. Each chunk of text is scanned once; a completed statement is parsed once and frozen. The result is not a new tree per chunk but a stream of small patches: create this node, set that prop, append these rows.
| Re-parse everything on each chunk | GistUI | |
|---|---|---|
| Work per chunk | grows with the answer so far | only the new text |
| What re-renders | the whole tree | the nodes a patch touched |
In Chrome, rendering a streaming answer takes 2–6× less main-thread work than OpenUI on OpenUI’s own sample screens. See Benchmarks for how these were measured.
4. The renderer draws only what changed
Every node has a stable id. Each rendered component subscribes to its own node, so a patch re-renders that component and nothing else. Patches are flushed once per animation frame.
While a part is still streaming, its interactive controls are locked (lockUntil), so a person cannot submit half a form.
5. At the end: autofix
Models make small, repeated mistakes: a prop that does not exist, a misspelled component, a reference nobody defined. Two layers deal with them.
- While reading, the format’s recovery rules say exactly how each slip is read (single quotes, a missing bracket,
key=value). Two implementations give the same tree for the same imperfect text. - When the stream ends,
autofixrepairs what is still wrong: it removes unknown props, corrects close enum values, renames misspelled components, drops dangling references and places unused statements. It is plain code and takes a few milliseconds.
On the benchmark, autofix takes validity from 88% to 100% with gpt-oss-120b. See Autofix and repair.
6. The runtime handles interaction
A program can hold state ($tab = "a"), read data through your tools (@query), and describe what a button does (do:[…]). The runtime evaluates these in the browser. It interprets expressions; it never compiles or evaluates code, so a strict Content-Security-Policy works.
Everything a person does reaches your app through one callback, onAction. See Actions and forms.
The packages
| Package | Role |
|---|---|
@gistui/core |
The language: lexer, parser, patches, validation, autofix, prompt generator. No dependencies. |
@gistui/catalog |
The 63 default component schemas and the system prompt built from them. |
@gistui/react, vue, svelte, solid, vanilla |
Renderers. Vue, Svelte and Solid are thin layers over vanilla. |
@gistui/styles |
One stylesheet for every renderer. |
@gistui/server |
Stream reading, validation and repair on the server. Optional. |
@gistui/chat |
A headless chat store, model adapters and React chat layouts. Optional. |
Full list: Packages.