A chat response is often the wrong output shape for a real product task.
If a user asks for a portfolio breakdown, a timeline, or a comparison table, the most useful answer is often a component, not a paragraph. That is the idea behind generative UI: let the model participate in selecting or configuring interface elements, not just generating text.
The important part is control. The model should not invent arbitrary UI. It should choose from a trusted set of components your system already knows how to render safely.
A Better Pattern
Instead of asking an LLM to produce freeform frontend markup, give it a constrained vocabulary:
type UiIntent =
| { type: "chart"; metric: string; period: "7d" | "30d" | "90d" }
| { type: "table"; entity: "orders" | "users"; limit: number }
| { type: "summary"; topic: string };
Then the application maps that output to real components:
function RenderIntent({ intent }: { intent: UiIntent }) {
switch (intent.type) {
case "chart":
return <RevenueChart metric={intent.metric} period={intent.period} />;
case "table":
return <EntityTable entity={intent.entity} limit={intent.limit} />;
case "summary":
return <SummaryCard topic={intent.topic} />;
}
}
That gives you the best part of generative UI without handing layout control to a stochastic system.
Where Server Components Help
React Server Components make this pattern more natural because the server can:
- fetch private data
- shape the result
- render the right component tree
without shipping every internal dependency to the browser.
The trade-off is that you still need a clear boundary between what the model decides and what the application guarantees.
Trade-Offs
Generative UI is useful when:
- the interface shape depends on user intent
- the set of supported components is known
- the application benefits from dynamic composition
It is less useful when the UI should be static, tightly workflow-driven, or heavily audited.
Further Reading