Generative UI
A model reply in chatz can contain live components: tables, status panels, charts, timelines, log viewers and more. The model writes them as JSON Patch lines in a fenced spec block, and the web app renders them inline, where they appear in the reply, while the reply streams. Tool results reach the screen the same way: the model reads the data and lays it out.
Every chat’s system message carries a generated guide that lists the components and the rules below, so any model that follows instructions can use them. The guide is built from the component catalog in web/src/lib/render/.
The wire format
The model writes a short explanation, then a ` ```spec ` fence with one RFC 6902 patch per line. The patches build a tree: /root names the top element, and each element under /elements/<key> has a type, props and the keys of its children.
Error rate on the API tier over the last hour.
```spec
{"op":"add","path":"/elements/rate","value":{"type":"Stat","props":{"id":null,"label":"Error rate","value":"0.4","unit":"%"},"children":[]}}
{"op":"add","path":"/elements/state","value":{"type":"Badge","props":{"id":null,"label":"Healthy","variant":"ok"},"children":[]}}
{"op":"add","path":"/elements/main","value":{"type":"Card","props":{"id":null,"title":"API tier"},"children":["rate","state"]}}
{"op":"add","path":"/root","value":"main"}
```
- The element key is its identity. Models set
props.idtonull, and the web app stamps the real ID from the key. - Text and several
specfences can mix in one reply; they render in the order written. Patch text is never shown as prose. - The renderer waits for each patch to be complete JSON before applying it, so a patch split across stream chunks or spread over several lines still works. An open fence renders the part of the tree that is already valid.
- A fence left unterminated reports an error only when the stream ends, and keeps the patches that were valid.
Components
The catalog is closed: a model cannot use a component that is not on this list, and props are declarative data, with no HTML, callbacks or chart code.
| Group | Components |
|---|---|
| Layout and text | Stack, Grid, Card, Text, Heading, Callout, Badge, Progress, Timeline, KeyValue |
| Summary and tables | Table, Stat, Sparkline |
| Charts | TimeSeriesChart, AreaChart, BarChart, DonutChart, FunnelChart, Gauge, ScatterPlot, Heatmap, Histogram, BoxPlot, Treemap, NetworkGraph |
| Large data | LogViewer |
The props of each component are defined in catalog.ts, and the exact text models receive is genui_instructions.gen.txt.
The guide tells models to use a chart only when it says more than prose or a small table, and to take every value from the conversation or tool results instead of inventing sample data.
Layout
- Components stay inside the message column. Wide tables and charts scroll inside their own box, never the page.
- Every
Gridcollapses to one column below 40rem, so dashboards reflow on a phone. - Colors come from the app’s theme tokens and follow the light or dark theme.
- The short reveal animation is off when the system asks for reduced motion.
Seeing it without a model
Demo mode answers seven fixed prompts with dashboards that use most of the catalog, which is the quickest way to see the components.
Changing the catalog
Adding or changing a component touches the catalog schema, the Svelte component, the registry and the prompt generator together, then make genui-prompt regenerates the guide. The renderer README has the steps and the tests to run.