GenUIRenderer
StableDraws a screen an assistant wrote, using only approved parts, and sends what the person does back.
Also known as generative ui, json renderer, dynamic ui
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
A summary that appears piece by piece
Orders this week
Orders
1,240
up, +4.2%bettervs last week
Late deliveries
12
up, +3worsevs last week
Nothing sent yet. Press the button to try it.
const [state, setState] = useState(emptySpec());
// For each streamed chunk:
setState((s) => applyChunk(s, chunk));
<GenUIRenderer
state={state}
pendingActionId={pending}
onAction={(e) => { setPending(e.actionId); sendToAssistant(e); }}
/>Pieces arrive out of order. The page shows placeholders for what is missing and fills them in. The button asks before it sends anything.
When to use it
Draws a small screen an assistant described as data, using only parts from an approved list and in our own look. Anything wrong becomes a quiet text card instead of breaking, and the person's choices go back to the assistant.
Made of: Parts from the approved list · Placeholders for pieces still arriving · Quiet card for parts that could not be shown · Waiting note · Inline confirm step
Use it for
- An answer that reads better as numbers, a chart or a table than as text.
- Asking the person to pick, set options or approve a plan inside the chat.
- A screen that streams in piece by piece.
Not for
- A whole page the assistant wrote in its own code: use SandboxedFrame.
- An answer one sentence can give: use Message.
- Showing who made the screen, with pin, redo and report: use GeneratedSurface.
Do and avoid
Orders this week
Orders
48
Late
12
Delivery update
Your parcel arrives on Tuesday.
Do
- Let the host check every part. Never draw a part that is not in the approved list.
- Mark anything hard to undo with confirm and a plain sentence of what will happen.
- Pass pendingActionId so people know the assistant is working on their choice.
- Offer a text answer too, in case the screen cannot be shown.
Avoid
- Don't let the assistant set colors, sizes or classes. Our look is not up to it.
- Don't skip the confirm step for sending, buying or deleting.
- Don't build a screen when one sentence would do.
- Don't assume the action worked until the assistant says so.
On a phone
- Everything renders in one column that fills the width, and the text in the fallback box breaks anywhere so it never pushes the page wide.
- A Group set to grid is one column on a phone, two from 640px and three from 1024px.
- The Confirm question and its buttons wrap, and the buttons keep their size and have a 44px tap area on a touch screen.
Seen in products
- Google A2UI: the agent sends a flat list of components and may only use a catalog the app approved, so the same data renders natively.
- Vercel json-render: the catalog defines the components the model may use, and a streaming format fills the screen in as it arrives.
- AG-UI: the app validates and mounts the tree the agent proposes, and interactions flow back to the agent.
- Microsoft Adaptive Cards: card authors own the content and the host owns the look and feel.
- OpenAI Apps SDK: a card shows at most two main actions, and write actions need confirmation.
- Google A2UI: agent-driven interfaces (opens in a new tab)
- A2UI project site (opens in a new tab)
- json-render (Vercel Labs) (opens in a new tab)
- AG-UI introduction (opens in a new tab)
- Adaptive Cards overview (Microsoft) (opens in a new tab)
- OpenAI Apps SDK UI guidelines (opens in a new tab)
- OpenAI Apps SDK UX principles (opens in a new tab)
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| staterequired | SpecState | none | The spec so far. Build it with applyChunk(state, chunk) as pieces stream in, or specFrom(root, nodes) for a finished one. |
| onAction | (event: GenUIActionEvent) => void | none | Receives { actionId, nodeId, type: 'press' | 'submit' | 'select' | 'change', value }. Called after any confirm step. Errors thrown here are caught. |
| pendingActionId | string | null | none | Set to the actionId while the assistant handles the action. The part that sent it shows a waiting note and its controls are disabled. |
| labels | GenUIRendererLabels | none | Overrides for visible text: fallbackTitle, loading, confirmQuestion, confirm, cancel, sending, missing. |
| className | string | none | Extra classes on the wrapper. |
"Show code under the preview gives the code for the example on the stage."
Tokens
Change one of these and every part that uses it follows.
--surface-sunken--border--attention-soft--attention-fg--radius-card
Built in
- Every part comes from the approved list and keeps its own labels and keyboard support.
- A part missing its words, such as a chart name or a button label, is not drawn. A quiet text card shows instead.
- While pieces are still arriving, screen readers hear "Still loading" once, not every new piece.
- The confirm step moves focus to Confirm, with Cancel next to it.
- The waiting note under a part is announced to screen readers.
What you need to do
- Pass pendingActionId while the assistant works on a choice, so people see and hear that it is waiting.
- Mark anything hard to undo with confirm and a plain sentence saying what will happen.
- Offer the answer as text too, in case the screen cannot be shown.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide