AI/TLDR

What Is Generative UI? Rendering Components from AI Output

Understand generative UI — letting the model return interactive components instead of plain text — and when it's worth the complexity.

INTERMEDIATE11 MIN READUPDATED 2026-06-13

In plain English

Most AI chat apps answer in one shape: a wall of text streaming onto the screen. That is fine for an explanation, but clumsy for a lot of real answers. If you ask for "my sales by region this quarter," a paragraph of numbers is worse than a bar chart. If you ask to "book the 3pm slot," a sentence describing the slot is worse than a button you can press.

Generative UI — illustration
Generative UI — developer-tech.com

Generative UI is the pattern where the model's output drives which interface component your app renders, not just what words appear. Instead of returning only prose, the model signals something like "show a chart with this data" or "show a confirmation card with these fields," and your app turns that signal into a real, interactive widget — a chart, a form, a product card, a map.

A useful analogy: a plain chatbot is a narrator reading you a report aloud. Generative UI is a stage manager. The model decides which prop the scene needs — a chart here, a form there — and your app, the stage crew, wheels the right object on stage. Crucially, the model never builds the widget itself; it just names which one to use and hands over the data. Your app owns the actual components.

Why it matters

Text is a great default, but some answers are simply better as interface. Generative UI matters because it lets one conversational surface produce the right output shape for each question, instead of forcing everything through paragraphs.

  • Some data is visual. Trends, comparisons, and distributions land instantly as a chart and slowly as prose. A model that can return a chart spec beats one that lists numbers.
  • Some answers need action. When the next step is do something — confirm a booking, pick an option, edit a value — a button or form is far better than asking the user to type a follow-up sentence the model then has to parse.
  • Structured answers deserve structure. A flight result, a product, a calendar event, or a user profile has fields. Rendering it as a card with labeled fields is clearer and more scannable than a run-on sentence.
  • It keeps the user in one place. Without generative UI, the model often answers "go to the dashboard and filter by region." With it, the dashboard widget appears right in the conversation.

Who should care? Builders of dashboards, internal tools, shopping and travel assistants, and any agent whose answers map to concrete objects or actions. If your domain has natural "things" — orders, tickets, charts, events — generative UI usually pays off.

How it works

Under the hood, generative UI is almost always built on function calling / tool calls — the same mechanism agents use, pointed at the UI instead of an external API. The flow has four moving parts: a catalog of components, a model that picks one, a validator, and a renderer.

Step 1 — You publish a component catalog

You decide up front which widgets the model is allowed to summon, and you describe each one as a tool with a strict input schema. A show_chart tool takes a chart type and data series; a show_booking_card tool takes a date, time, and price. The schema is the contract: it says exactly what props the component needs.

Step 2 — The model picks a component and fills the props

Given the user's question and the catalog, the model decides whether a component fits and, if so, which one — then emits a tool call with the props filled in. This is the same skill as structured outputs: the model returns machine-readable JSON that matches your schema, not free prose.

Step 3 — You validate, then render

Your app receives the tool call, validates the arguments against the schema (never trust them raw), looks up the matching component in your catalog, and renders it with those props. The model name show_chart maps to your <Chart> React component — code you wrote and control.

Notice what the model does not do: it never writes HTML, CSS, or JSX, and it never invents a new component. It chooses a name from your menu and supplies typed data. The renderer is a simple switch over known component names — anything unrecognized is dropped or shown as a fallback.

the renderer is a tiny switch over your catalogtypescript
// The model returns a tool call; you map its name to a real component.
function renderWidget(call: { name: string; args: unknown }) {
  switch (call.name) {
    case "show_chart":
      return <Chart {...ChartProps.parse(call.args)} />;   // validate, then spread
    case "show_booking_card":
      return <BookingCard {...BookingProps.parse(call.args)} />;
    default:
      return null; // unknown name → render nothing (or a text fallback)
  }
}

A worked example: "show me sales by region"

Walking one request end to end makes the pattern concrete. Suppose your assistant can answer business questions and you want chart answers when they help.

Define the tool

a single generative-UI toolpython
show_chart = {
    "name": "show_chart",
    "description": "Render a chart when the answer is a trend or comparison.",
    "input_schema": {
        "type": "object",
        "properties": {
            "chart_type": {"type": "string", "enum": ["bar", "line", "pie"]},
            "title": {"type": "string"},
            "series": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "label": {"type": "string"},
                        "value": {"type": "number"},
                    },
                    "required": ["label", "value"],
                },
            },
        },
        "required": ["chart_type", "series"],
    },
}

What the model returns

You pass the user's question, the data the model is allowed to use, and the tool list. The model decides a bar chart fits and emits a tool call whose arguments match the schema exactly:

the tool call arguments (validated before use)json
{
  "chart_type": "bar",
  "title": "Sales by region — Q2",
  "series": [
    { "label": "North", "value": 412000 },
    { "label": "South", "value": 318000 },
    { "label": "West",  "value": 502000 }
  ]
}

Your app parses this, confirms every value is a number and chart_type is one of the allowed three, then renders your real <BarChart> with the data. The user sees a chart; the model only ever produced a small, checked JSON object. If the question had been "what is our refund policy?", the model would skip the tool and just answer in text — which is exactly what you want.

Plain text vs generative UI: when to use which

The honest answer is that most replies should still be text, and you sprinkle in components only where they clearly beat prose. Use this rough guide.

SituationBetter choiceWhy
Explanation, reasoning, adviceStreaming textProse is the natural shape; widgets add nothing
Trend or comparison of numbersChart componentA chart is read in one glance
A result with named fields (flight, order, event)Card componentLabeled fields beat a run-on sentence
Next step is an action (confirm, pick, edit)Button / formOne click beats typing a follow-up
A list of choices to pick fromSelectable listReduces ambiguity vs. "reply with the number"
Anything you can't render safelyText fallbackNever block the answer on a missing widget

A practical rule: start every project as a text chatbot. Add a generative-UI component only when you can name a specific question whose answer is genuinely worse as a paragraph. Let the components earn their place one at a time.

Common pitfalls and how to avoid them

Generative UI fails in ways a text app never does, because you are now rendering data the model produced. Almost every pitfall comes back to one rule: treat model output as untrusted input.

  • Rendering raw model output. Never spread the model's arguments straight into a component. Validate every field against the schema (with a library like Zod, Pydantic, or JSON Schema) and clamp or reject bad values before render.
  • Letting the model emit markup. If a tool prop is meant to be plain text but you render it as HTML, the model — or text it copied from a retrieved document — can inject markup. Render model strings as text, never as dangerouslySetInnerHTML.
  • No fallback path. Schemas drift, models occasionally return a malformed or unknown call. If the renderer can't match a component or validation fails, fall back to a plain-text summary rather than a blank space or a crash.
  • Too many components. A catalog of 30 lookalike widgets confuses the model about which to pick. Keep it small and distinct; merge near-duplicates into one component with a variant prop.
  • Forgetting injection risk. If your tool data comes partly from retrieved or user-supplied documents, a prompt injection can try to steer which widget renders or what it shows. Validate values and keep server-side guards on any action a widget can trigger.

Going deeper

The catalog-plus-tool-call pattern above is the durable core. Once it clicks, a few more advanced directions are worth knowing.

Streaming props progressively. Tool-call arguments stream token by token, so you can render a widget's skeleton the instant the component name is known and fill fields as they arrive. Done well this feels faster than waiting for the whole object; done badly it causes layout jumps, so reserve space and animate values in.

Server-driven vs client-driven rendering. In a server-rendered setup, the server resolves the tool call into a serialized component description and streams it to the client (the approach popularized by frameworks like Vercel's AI SDK with React Server Components). In a client-driven setup, the client receives the raw tool call and maps it to local components. Both work; the trade-off is where your component catalog and validation live.

Interactive round-trips. A truly useful widget often sends data back: the user edits a form field or clicks an option, and that becomes the next message to the model. Designing this loop — widget renders, user interacts, interaction feeds back as a new tool result — is what separates a static card from a real interface. Treat each interaction as a new validated input, the same way you treat the model's output.

Generative UI vs. "AI writes the UI." Be careful with terminology: this article is about a model selecting from your fixed components at runtime. A different, riskier idea is a model generating fresh component code (JSX/HTML) to render live. The first is bounded and safe because your catalog is the ceiling; the second is powerful but opens a large attack surface and is far harder to make reliable. For production apps, the bounded version is almost always what you want.

The lasting lesson mirrors the rest of building AI apps: the model is a great chooser of structure and a poor renderer of it. Keep the model's job to picking a component and supplying validated data, keep rendering and authorization firmly in your own code, and reach for a widget only when the answer truly has a shape. Most of the time, well-streamed text is still the best UI you can ship.

FAQ

What is generative UI in AI apps?

Generative UI is a pattern where the AI model's output decides which interface component your app renders, not just what text appears. Instead of returning only prose, the model emits a structured signal (usually a tool call) like show a chart or show a form, and your app renders the matching widget from a catalog you control. The model picks the component and its data; your code does the actual rendering.

How do you render React components from LLM output?

You expose each component as a tool with a strict input schema, let the model emit a tool call choosing one and filling its props, then validate those props and map the tool's name to a real component in a small switch statement. For example show_chart maps to your <Chart> component. The model never writes JSX — it only selects a name and supplies validated data.

Is generative UI the same as the AI writing the UI code?

No, and the difference matters for safety. Generative UI normally means the model selects from your fixed catalog of pre-built components at runtime, which is bounded and safe. A model generating fresh component code (JSX or HTML) to render live is a different, riskier idea with a much larger attack surface. Most production apps use the bounded, catalog-based version.

When should I use generative UI instead of plain text?

Use it when the answer has a natural shape — a chart for trends, a card for a result with named fields, a button or form when the next step is an action. Stick with streaming text for explanations, reasoning, and Q&A, where a widget adds nothing. A good rule is to start as a text chatbot and add components only where a specific answer is clearly worse as a paragraph.

How do you keep generative UI safe?

Treat the model's output as untrusted input. Validate every prop against the component's schema before rendering, render model strings as text rather than HTML, keep a text fallback for unknown or malformed calls, and never let the model choosing to show an action button count as authorizing that action — re-check permissions and validate on the server when it is pressed.

Does generative UI rely on tool calling?

Almost always, yes. The standard implementation uses the same function-calling / tool-call mechanism agents use, but pointed at your UI components instead of external APIs. Each component is described as a tool with an input schema, and the model returns a tool call whose arguments are the component's props.

Further reading