v0.2.1Autofix now reads past a bad token: 99.5% valid on gpt-5-nano

The rendering engine for generative UI

Your model answers in a compact language for interfaces. GistUI renders it as real components while it streams.

Install for
dashboard.gistui629 tokens
  1. root = Page(top, kpis, Grid(Cell(revenue, span:2), plans, cols:3), gap:lg, width:wide, accent:orange)
  2. top = Box(Header("Acme Cloud", "SaaS metrics · last 30 days", eyebrow:"Dashboard", size:lg), Buttons(Button("Export", icon:download, v:secondary, size:sm), Button("Invite team", icon:users, size:sm)), dir:row, justify:between, align:center, wrap)
  3. kpis = Grid(mrr, customers, nrr, churn, cols:4)
  4. mrr = Stat("Monthly recurring revenue", "$482.3k", "+12.4%", note:"vs last month", icon:dollar, spark:[310, 322, 341, 356, 372, 390, 405, 421, 438, 452, 469, 482])
  5. customers = Stat("Active customers", "3,941", "+214", note:"this month", icon:users, spark:[2980, 3060, 3140, 3230, 3310, 3390, 3480, 3560, 3650, 3730, 3820, 3941])
  6. nrr = Stat("Net revenue retention", "118%", "+3pt", note:"trailing 12 months", icon:trending-up, spark:[108, 109, 111, 112, 112, 114, 115, 115, 116, 117, 117, 118])
  7. churn = Stat("Logo churn", "1.8%", "-0.4pt", note:"lower is better", icon:users, invert, spark:[3.1, 2.9, 2.9, 2.7, 2.6, 2.5, 2.3, 2.2, 2.1, 2.0, 1.9, 1.8])
  8. revenue = Card(Chart(rev, type:area, stacked, title:"Revenue by plan", subtitle:"MRR, thousands of dollars", height:280))
  9. plans = Card(Chart(mix, type:donut, title:"Plan mix", subtitle:"Share of MRR", height:220, legend))
  10. rev = |Month|Starter|Pro|Enterprise
  11. |Oct|42|118|150
  12. |Nov|44|122|156
  13. |Dec|45|127|169
  14. |Jan|46|131|179
  15. |Feb|47|136|189
  16. |Mar|48|141|201
  17. |Apr|49|145|211
  18. |May|50|150|221
  19. |Jun|51|154|233
  20. |Jul|52|158|242
  21. |Aug|53|162|254
  22. |Sep|54|166|262
  23. mix = |Plan|Share
  24. |Enterprise|54
  25. |Pro|34
  26. |Starter|12
A SaaS dashboard rendered by GistUI: a header with two buttons, four KPI cards with sparklines, a stacked area chart of revenue by plan and a donut chart of the plan mix.A SaaS dashboard rendered by GistUI: a header with two buttons, four KPI cards with sparklines, a stacked area chart of revenue by plan and a donut chart of the plan mix.
stock.gistui330 tokens
  1. $range = "1M"
  2. root = Page(Frame(head, pick, chart, stats, actions, style:{fill:"surface", stroke:"border", radius:20, pad:24, gap:18}), width:narrow)
  3. head = Frame(Frame(Text("Apple Inc. · AAPL", style:{size:14, color:"muted"}), Text("$228.52", style:{size:36, weight:600, tracking:-0.03, leading:1.1}), Text("+$2.81 (1.24%) today", style:{size:14, weight:500, color:"success"})), Tag("NASDAQ", pill), style:{layout:"row", justify:"between", align:"start"})
  4. pick = RadioGroup("range", "Range", ["1D", "1W", "1M", "1Y"], v:segmented, bind:$range)
  5. chart = Chart(month, type:line, height:200)
  6. stats = KeyValue(statData, cols:2)
  7. actions = Buttons(Button("Buy", icon:plus), Button("Set alert", icon:bell, v:secondary))
  8. statData = |Key|Value
  9. |Open|$226.10
  10. |Day range|$225.40 – $229.12
  11. |Market cap|$3.47T
  12. |P/E|34.6
  13. month = |Date|Price
  14. |Sep 2|214.3
  15. |Sep 9|218.9
  16. |Sep 16|216.2
  17. |Sep 23|221.4
  18. |Sep 30|228.5
A stock card rendered by GistUI on a phone: the price and today's change, a range switcher, a line chart, four key figures and two buttons.A stock card rendered by GistUI on a phone: the price and today's change, a range switcher, a line chart, four key figures and two buttons.

Works with the model and
the provider you already use

One language, every kind of screen

Dashboards, forms, reports, slide decks and guides. Each one is a real GistUI answer, rendered with the default components.

1,574
tokens
22
statements
57
components
Dashboard, rendered by GistUIDashboard, rendered by GistUI

Engine

Everything between the stream and the screen

GistUI is a small runtime between your model and your components. It reads the answer, checks it, and draws it.

Streaming

Each statement is parsed once, as it arrives. The screen starts to fill in with the first line.

Autofix

When a model slips, the program is repaired in code and shown in place. There is no second model call.

Any framework

React, Vue, Svelte, Solid or vanilla JS, with the same 63 components in each.

Safe by default

A program cannot run code. Tools that change something run only when a person presses a button.

Small

The runtime and the default components load together. Heavier parts load on demand.

Benchmarks

Measured, not claimed

Against OpenUI, on its own sample screens and its own benchmark. Every script and every answer is in the repository.

Rendering a streaming answer

Main-thread time in Chrome · lower is better

2–6× less work
OpenUI
2,204 ms
GistUI
492 ms

The dashboard from OpenUI's own samples, streamed at about 100 tokens a second: 2,204 ms of work for OpenUI, 492 ms for GistUI. Across all eight screens tested, GistUI needed 2 to 6 times less.

Tokens per screen

System prompt + answer, gpt-oss-120b · lower is better

21% fewer
OpenUI
5,618
GistUI
4,461

The same 46 screens on OpenUI's benchmark: a 3,874-token prompt and 587-token answers for GistUI, 5,017 and 601 for OpenUI.

First-load JavaScript

KB gzip, default components · lower is better

9× smaller
OpenUI
699 KB
GistUI
74 KB

Renderer plus the default component library, minified and gzipped, React excluded. Measured the same way on both (@openuidev/react-ui 0.16.3).

How it was measured, and every result

Get started

On screen in three steps

Install the package for your framework, give your model the system prompt, and point the component at the response.

  1. 1. Install

    Add @gistui/react and the stylesheet to your app.

  2. 2. Give your model the prompt

    On the server, prompt().text from @gistui/catalog is the system prompt.

  3. 3. Render the answer

    Point the component at the response stream. Buttons and forms come back through one callback.

// npm install @gistui/react @gistui/styles

import "@gistui/styles/styles.css";
import { GistUI } from "@gistui/react";
import { ui } from "@gistui/react/ui";

<GistUI library={ui} stream={response.body} onAction={handle} />

FAQs

Your questions answered.

Something else? Ask on GitHub.

Does it work with my model provider?

Yes. GistUI only needs a text stream. It reads OpenAI-compatible, Anthropic, AI SDK and AG-UI streams, so gateways and direct provider APIs both work.

What happens when someone submits a form or presses a button?

Your app gets it through one callback, onAction. A form submit arrives with typed values, a JSON Schema for them and a readable summary.

Is it safe to render what a model wrote?

A program is treated as untrusted. It cannot run code, read-only tools and tools that change something are separate, images load only from hosts you allow, and nesting, loops and sizes all have limits.

Can I use my own components and design system?

Yes. Replace any built-in component with yours, add new ones with a schema, or restyle the defaults with tokens. There is a worked example with shadcn/ui and Tailwind.

Is it only for chat?

No. Anything that can show a stream of text can show GistUI: a dashboard that answers a question, a report, a form built for one task. The chat layout is one optional package.