Generative UI: Rendering Components from Model Output
Go beyond text: have the model choose and fill real UI components — cards, forms, charts — while your code stays in charge of what renders.
Ask a text-only assistant about an order and you get a paragraph: "Order DM-10442 totals $89.00, was paid on March 3, and is refundable until April 2." It's correct, and it's worse than the order card your app already has. The agent has to read it, can't click anything, and can't tell whether those numbers came from the database or the model's imagination.
Generative UI means the model's output decides which components to show and how to fill them, instead of producing only text. Done well, the assistant answers with a real order card, a pre-filled refund form, or a chart, rendered by your components with your data. In this lesson you'll build that for Deskmate's assistant panel with the Vercel AI SDK, and you'll learn the rule that keeps it safe: the model chooses, your code renders.
The model chooses, your code renders
There's a spectrum of how much control you hand the model:
| Approach | What the model outputs | Risk |
|---|---|---|
| Text only | Prose, maybe markdown | Low, but data is unverifiable and nothing is clickable |
| Component selection | "Show the order card for DM-10442" | Low: your code fetches the data and renders a known component |
| UI spec | A layout from a closed vocabulary of blocks | Moderate: you validate the spec against a schema |
| Generated markup | Raw HTML, JSX, or CSS | High: injection, broken layouts, invented data |
This lesson covers the middle two. The last one, letting a model write HTML or JSX that you render live in your product, is almost never the right call for a product feature. It invites cross-site scripting, makes every response look different, and gives you no guarantee the numbers on screen are real. If you need it (say, a prototyping tool), it belongs in a sandbox; see Securing Model Output.
The principle behind the middle two approaches: the model supplies identifiers and choices; your server supplies the data; your components supply the pixels. The model says "order DM-10442." Your server looks up DM-10442 in the database, scoped to the user's workspace. Your OrderCard component renders the result. If the model gets the order number wrong, you show "not found," not a fabricated order.