Chat is the first thing most teams build into an AI product, and the first thing most teams overbuild.
It is familiar and flexible. A text box accepts any request without a designer planning the screen first. For exploration and one-off questions, that is exactly right. The trap is treating the message stream as the entire product, so every result, choice, and action has to be expressed as more text.
Where chat-only breaks down
Watch what happens when real work moves into a chat-only interface:
- a comparison between three options becomes a wall of prose you re-read to keep straight;
- a generated document lives inside a message, so you copy it out by hand to use it;
- a choice between paths becomes “reply with 1, 2, or 3”;
- an action that sends an email or changes a record becomes an ambiguous sentence with no preview;
- the current state of the work lives in scrollback, and you reconstruct it by scrolling.
None of this is a model problem. The model understood the task. The interface had nowhere to put the answer except back into the transcript.
The companies that defined chat-first AI noticed this early. Anthropic’s Claude added Artifacts, a panel beside the conversation that renders documents, code, and diagrams as editable objects. OpenAI’s ChatGPT added Canvas, a workspace for writing and code that sits next to the chat rather than inside it. GitHub Copilot shows code as diffs you accept or reject, not as chat replies. The pattern is consistent: the conversation stays, but the important output moves to a surface built for it.
The better frame: chat as the control layer
Treat chat as the conversational edge of a larger interface, not the container for everything.
A person can start with a message. The product’s job is to move useful information out of that message and into a surface that fits the task:
- cards for options;
- tables for comparison;
- forms for missing input;
- diffs and previews for generated work;
- a confirmation step for anything that changes external state.
The model still drives the experience. It just should not be forced to express every kind of result as prose.
A pattern: route on what the response is
When you design an AI feature, the useful question is not “what should the model say.” It is “what kind of thing is this response,” because each kind implies a different surface.
flowchart LR
U["User intent"] --> C["Chat surface"]
C --> M["Model"]
M --> S{"Response type"}
S -->|"Answer"| A["Message bubble"]
S -->|"Options"| O["Option cards"]
S -->|"Draft"| D["Editable artifact"]
S -->|"Action"| X["Confirmation step"]
A recommendation needs evidence and tradeoffs. A set of options needs selectable cards. A draft needs an editor. A pending action needs a preview and a confirm. The message is the explanation. The structured surface is the object you act on.
Architecture: the model fills a contract, the frontend owns the surface
For this to work, the frontend has to understand typed output, not just render bubbles. The cleanest approach is to have the model return a typed result and let the frontend decide how to present it.
flowchart TB
Model["Model response"] --> Router{"result.type"}
Router -->|"message"| Bubble["Chat bubble<br/>prose, markdown"]
Router -->|"options"| Cards["Option cards<br/>compare, select"]
Router -->|"draft"| Editor["Editable artifact<br/>inspect, edit"]
Router -->|"action_request"| Confirm["Confirmation step<br/>preview, approve"]
type AssistantResult =
| { type: "message"; markdown: string }
| { type: "options"; title: string; items: Option[] }
| { type: "draft"; artifact: DraftArtifact }
| { type: "action_request"; action: ActionPreview };
The model does not choose the font, the card layout, or the button colors. It fills the contract. The frontend owns presentation. This is the same separation that typed API responses gave ordinary frontends. The difference is that the producer is now a probabilistic model, which makes the contract more important, not less.
Current tooling already pushes this way. OpenAI’s structured outputs and Anthropic’s tool use let a model return data that conforms to a schema you define. The Vercel AI SDK can stream typed objects from a Zod schema, and its React Server Components support can stream UI directly. Once the output is typed, routing it to the right surface is ordinary frontend work.
Product implication
When chat is one surface among several, the product feels calmer.
People can still talk in plain language, but the work they care about becomes inspectable. They compare options in a table instead of in their head. They edit a draft in place instead of copying it out. They approve an action from a preview instead of trusting a sentence. The conversation becomes the way they steer the product, not the place they read every result.
What to watch next
Today most teams define their own result.type by hand, per app. A few efforts want to standardize the handoff from agent to interface. AG-UI, an open protocol from CopilotKit, streams agent events to a frontend in a defined shape. MCP-UI extends Anthropic’s Model Context Protocol so a server can return UI, not just text. These are early, and worth watching with some skepticism. A shared contract for “agent output to interface surface” would turn a lot of per-app glue into a reusable layer, if it holds up in practice.
The next post goes one level down, into how the model’s structured output and your components should be designed together.