Structured Output

When an agent uses responseType on a next-message block, the client receives a UIObjectPart instead of a UITextPart. This enables rich, custom UI for typed responses.

How It Works

  1. The protocol defines a type and uses it as responseType:
yaml
  1. The agent generates a JSON response matching the schema
  2. The client SDK receives a UIObjectPart with progressive JSON parsing
  3. Your app renders custom UI based on the typeName

The UIObjectPart

typescript

During streaming, partial contains the progressively parsed object. When streaming completes, object contains the final validated result.

Building a Renderer

Create a renderer component for each type you want to customize:

tsx

Renderer Registry Pattern

For apps with multiple response types, use a registry to map type names to renderers:

tsx

Using in Part Renderer

Integrate with your part renderer:

tsx

Handling Streaming State

During streaming, the object is progressively parsed. Handle incomplete data gracefully:

tsx

Error Handling

If JSON parsing fails, status will be 'error' with details in error:

tsx

Complete Example

Here's a complete chat interface with structured output support:

tsx

Best Practices

Design types for progressive rendering:

Structure your types so the most important fields stream first. Property order in YAML is preserved during streaming.

yaml

Keep renderers resilient:

Handle missing fields gracefully since partial objects may have undefined properties:

tsx

Use TypeScript for type safety:

Define TypeScript interfaces matching your protocol types:

typescript

Test with slow connections:

Streaming is more noticeable on slow connections. Test your UI with network throttling to ensure a good experience.

Type Requirements

The responseType in your protocol must be an object type (regular custom type with properties).

The following cannot be used directly as responseType:

  • Discriminated unions — LLM providers don't allow anyOf at the schema root
  • Array types — Must be wrapped in an object
  • Primitivesstring, number, etc. are not valid

If you need variant responses, wrap the discriminated union in an object:

yaml

If you need the LLM to return an array, wrap it in an object:

yaml

See Types - Structured Output for more details on defining response types.