Skip to content

Research note

Structured outputs need structured interfaces

How schemas, components, and model responses work together to make AI interfaces predictable.

Series · Part 2 of 4

AI Interface Foundations

Four posts that build on each other: chat as a surface, structured outputs and components, trust boundaries, and durable workflows. From working model to working product — much of that work lives in the interface.

  1. 1 Chat is a surface, not the product
  2. 2 Structured outputs need structured interfaces
  3. 3 Trust boundaries in AI interfaces
  4. 4 From prompt box to workflow

Getting a model to return clean JSON is now a solved-enough problem. Deciding what the frontend does with that JSON is where most teams stop too early.

For a while, structured output was genuinely hard. You asked for JSON and sometimes got a code fence, a trailing comment, or an invented field. That changed. OpenAI’s structured outputs constrain decoding to a JSON Schema you supply, so the response is valid and matches the shape. Anthropic’s tool use returns structured input for the tools you define. The Vercel AI SDK’s generateObject and streamObject validate against a Zod schema and hand you a typed object. Reliable structure at the API boundary is the new baseline.

Here is the part teams miss. That structure is a frontend opportunity, and it usually gets thrown away.

The missed interface

The common shape of the bug:

  1. The backend defines a schema so the model’s output is reliable.
  2. The model returns a clean object that matches it.
  3. The frontend receives the object.
  4. The product renders a paragraph of natural language describing the object.

Step four undoes the value of steps one through three. You had typed data with fields for confidence, tradeoffs, and a recommended action, and you flattened it back into prose the user has to parse by eye.

If the model returns recommendations, the interface should show recommendations. If it returns tasks, show tasks with status. If it returns a plan, show steps and dependencies. The schema already told you the shape. The UI should honor it.

The pattern: design three layers together

A strong AI interface keeps three layers aligned:

flowchart LR
  A["Model schema"] --> B["Component contract"]
  B --> C["User decision"]
  C -->|"shapes next output"| A

These are one design problem, not three. The schema constrains what the component can render. The component constrains what the user can do. The user’s action decides what the model should produce next. When one layer drifts from the others you get the usual mismatches: a schema with fields nothing renders, or a component asking for data the schema never returns.

Start from the decision and work backwards. If the user’s job is to pick one recommendation and act on it, the schema should carry exactly what that decision needs.

type Recommendation = {
  title: string;
  rationale: string;
  confidence: "low" | "medium" | "high";
  tradeoffs: string[];
  nextAction: {
    label: string;
    kind: "review" | "apply" | "schedule" | "save";
  };
};

This shape does two jobs at once. It tells the frontend what to render, and it tells the product what decision it is asking the user to make. confidence is a restrained signal, not a number to over-trust. tradeoffs is inspectable detail you can hide behind a disclosure. nextAction.kind maps to a real button. Nothing here is decorative.

Component thinking

The component should reflect the meaning of the output, not just its fields.

A recommendation card built from that schema might show the title, a one-line rationale, confidence as a quiet badge, tradeoffs behind “show details,” and a single primary action. The model is not designing the card. It is filling a contract the product team designed, and the same contract renders the same way every time, whatever the content.

That last point is what makes the interface feel stable. The model can vary the content freely while the product shape stays constant. You test the component once against the schema, not against every sentence the model might produce.

Why this matters

Schemas make model behavior easier to test. Components make model behavior easier to use. Designed together, they make the interface predictable, and predictability is most of what users mean by trust.

People do not only want better answers. They want answers in a form they can scan, compare, challenge, and act on. A typed object rendered as a real component gives them that. A paragraph that happens to contain the same facts does not.

What to watch next

The real risk is over-structuring too early. Not every response needs a rigid schema. Open-ended exploration should stay loose, and forcing it into cards makes the product feel like a form. The signal to add a contract is repetition. The moment an AI feature supports a recurring workflow, a decision, or an action, the output has earned a real interface.

The other thing worth watching is streaming. Tools like streamObject emit partial objects as the model generates them, so components have to render a half-filled schema gracefully. Designing a component that still looks right at forty percent complete is becoming part of this work.

Once the output is structured and rendered well, the next question is control: which outputs are safe to act on automatically, and which need a human. That is the next post, on trust boundaries.

Previous
Trust boundaries in AI interfaces
Next
Chat is a surface, not the product