Completion and Object Generation
Purpose and Scope
Completion and object generation are two UI patterns for model output that is narrower than a full chatbot. A completion interface sends one prompt or input value and renders a streamed text answer, which is useful for autocomplete, drafting, rewriting, or command-style interactions. An object generation interface streams a typed JSON shape into the UI, which is useful when the interface needs fields, lists, classifications, or other structured data rather than a single text block. Together, these patterns let product code choose the smallest interaction model that fits the user task instead of defaulting every experience to chat.
The source-backed workflow on this page is the AI SDK UI object generation guide. It presents useObject as an experimental hook for interfaces that represent a structured JSON object while it is being streamed, and it frames the guide around generating UI for structured data on the fly. Completion follows the same UI philosophy: the hook owns request state and streaming updates so application components can focus on rendering user controls and model output. Sources: content/docs/04-ai-sdk-ui/08-object-generation.mdx
Relevant Source Files
content/docs/04-ai-sdk-ui/08-object-generation.mdx— Defines the Object Generation guide, the experimental status and framework availability ofuseObject, the shared Zod schema pattern, the React client example, the server route example, and the enum output mode pattern.
Core UI Primitives
For completion, the first primitive is useCompletion, which the official UI reference describes as a hook for text-completion capabilities. It streams text completions from an AI provider, manages chat-style input state for the prompt, and updates the UI as new text arrives. The default endpoint convention is /api/completion, with configuration for values such as an id, initial input, initial completion, and an onFinish callback that receives the prompt and final completion. Use this pattern when the output is plain text and the UI does not need a typed object contract.
For object generation, the first primitive is experimental_useObject, imported as useObject from the framework UI package in the React example. The guide explicitly marks useObject as experimental and available only in React, Svelte, and Vue. Its client contract centers on an API route and a schema. The hook exposes the current partial object and a submit function; the component calls submit with an input string and renders fields as the object arrives. Because streaming JSON can be incomplete, the example uses optional chaining and handles undefined values in JSX. Sources: content/docs/04-ai-sdk-ui/08-object-generation.mdx
The schema is the bridge between client and server. The guide recommends putting it in a separate file imported on both sides, so the UI and route agree on the object shape. In the notifications example, notificationSchema is a Zod object with a notifications array. Each notification has a name string described as a fictional person and a message string described as a message without emojis or links. That schema is not just documentation; it drives server output validation and gives the client a typed structure to render safely. Sources: content/docs/04-ai-sdk-ui/08-object-generation.mdx
Object Generation Execution Flow
A typical object-generation flow starts with a shared schema file, continues through a client component, and finishes in a server route that streams model output back to the UI. In the guide, the client component is a use client page that imports experimental_useObject as useObject from @ai-sdk/react and imports notificationSchema from the API route directory. The hook is initialized with api: '/api/notifications' and the schema. A button calls submit('Messages during finals week.'), and the component maps over object?.notifications to show each partial notification as soon as it exists. Sources: content/docs/04-ai-sdk-ui/08-object-generation.mdx
On the server, the route reads the submitted context with await req.json(), calls streamText, and uses Output.object({ schema: notificationSchema }) to request structured output from the model. The prompt concatenates a fixed instruction with the submitted context, asking for three notifications for a messages app. The route then converts the model stream with toTextStream({ stream: result.stream }) and returns it through createTextStreamResponse. The documented route also exports maxDuration = 30, which signals that the streaming response may run for up to thirty seconds in the deployment environment. Sources: content/docs/04-ai-sdk-ui/08-object-generation.mdx
// Shared schema shape from the documented pattern:
import { z } from 'zod';
export const notificationSchema = z.object({
notifications: z.array(
z.object({
name: z.string(),
message: z.string(),
}),
),
});Completion Versus Structured Object UI
Choose completion when the model result is naturally text and the UI only needs to append or replace a string. Examples include summarizing a paragraph, generating a title, completing a sentence, rewriting tone, or producing a short answer. In that shape, the application’s main concerns are prompt submission, loading state, cancellation or retry behavior, and final text handling. The UI does not need to validate a nested structure, and the server route can stream text directly from the model response to the client-facing hook.
Choose object generation when the interface needs stable fields. The notifications demo illustrates this difference: rendering a list of cards requires a collection, and each card needs a name and message. A plain completion could return a markdown list, but the UI would then need to parse text or trust formatting. With useObject, the schema establishes the shape in advance, the server asks the model for that shape through Output.object, and the client renders typed partial values as they arrive. This gives the UI an incremental structured contract rather than a best-effort text convention. Sources: content/docs/04-ai-sdk-ui/08-object-generation.mdx
The most important rendering constraint is partiality. During streaming, the object may be absent, the array may not be complete, and individual properties may still be undefined. The guide calls this out directly and demonstrates optional checks in JSX. Application components should therefore render defensively: gate list rendering on optional properties, avoid assuming array items are complete, and design placeholders or progressive states for fields that arrive later. This is not an error state; it is the normal consequence of streaming a structured object into a live interface. Sources: content/docs/04-ai-sdk-ui/08-object-generation.mdx
Enum Output Mode
The guide also documents an enum output mode for classification and categorization tasks. In this mode, the schema must be an object with enum as the key, and that key must contain a Zod enum with the allowed values. The example classifies a statement as either true or false using z.object({ enum: z.enum(['true', 'false']) }). The client submits the statement The earth is flat, disables the button while isLoading is true, and renders Classification: {object.enum} when a streamed value exists. Sources: content/docs/04-ai-sdk-ui/08-object-generation.mdx
Enum mode is a good fit when the user experience is about decisioning rather than prose. Sentiment, priority, moderation category, binary validation, routing labels, and feature flags can all be represented as a small closed set. Keeping the set in the schema is valuable because both the UI and the route can reason about the same allowed values. It also avoids asking downstream code to interpret arbitrary text such as “probably false” when the interface needs one of a few exact choices.
const { object, submit, isLoading } = useObject({
api: '/api/classify',
schema: z.object({ enum: z.enum(['true', 'false']) }),
});Compact Reference
| Pattern | Client primitive | Server primitive | Best for | Key constraint |
|---|---|---|---|---|
| Completion | useCompletion | Text streaming route | Single text result, autocomplete, rewriting, summaries | Output is text, not a typed object |
| Object generation | experimental_useObject / useObject | streamText with Output.object({ schema }) | Lists, cards, forms, typed structured UI | Render partial objects defensively |
| Enum object generation | useObject with z.object({ enum: z.enum([...]) }) | Structured object streaming route | Classification and routing decisions | Schema key must be enum |
Use object generation by first extracting the Zod schema into a shared file, then wiring the client hook to the target API route, and finally configuring the server route with Output.object. Use completion when the endpoint and component only need to stream text. If you are building a richer conversational interface with multiple messages, tools, persistence, metadata, or resumable streams, move from these focused primitives to the chat and stream protocol pages instead. Sources: content/docs/04-ai-sdk-ui/08-object-generation.mdx