📄 ai-sdk/docs/ai-sdk-ui/object-generation

File: object-generation.md | Updated: 11/15/2025

Source: https://ai-sdk.dev/docs/ai-sdk-ui/object-generation

AI SDK

Menu

v5 (Latest)

AI SDK 5.x

AI SDK by Vercel

AI SDK 6 Beta

Foundations

Overview

Providers and Models

Prompts

Tools

Streaming

Getting Started

Navigating the Library

Next.js App Router

Next.js Pages Router

Svelte

Vue.js (Nuxt)

Node.js

Expo

Agents

Agents

Building Agents

Workflow Patterns

Loop Control

AI SDK Core

Overview

Generating Text

Generating Structured Data

Tool Calling

Model Context Protocol (MCP) Tools

Prompt Engineering

Settings

Embeddings

Image Generation

Transcription

Speech

Language Model Middleware

Provider & Model Management

Error Handling

Testing

Telemetry

AI SDK UI

Overview

Chatbot

Chatbot Message Persistence

Chatbot Resume Streams

Chatbot Tool Usage

Generative User Interfaces

Completion

Object Generation

Streaming Custom Data

Error Handling

Transport

Reading UIMessage Streams

Message Metadata

Stream Protocols

AI SDK RSC

Advanced

Reference

AI SDK Core

AI SDK UI

AI SDK RSC

Stream Helpers

AI SDK Errors

Migration Guides

Troubleshooting

Copy markdown

Object Generation

===========================================================================================

useObject is an experimental feature and only available in React, Svelte, and Vue.

The useObject hook allows you to create interfaces that represent a structured JSON object that is being streamed.

In this guide, you will learn how to use the useObject hook in your application to generate UIs for structured data on the fly.

Example


The example shows a small notifications demo app that generates fake notifications in real-time.

Schema

It is helpful to set up the schema in a separate file that is imported on both the client and server.

app/api/notifications/schema.ts

import { z } from 'zod';
// define a schema for the notificationsexport const notificationSchema = z.object({  notifications: z.array(    z.object({      name: z.string().describe('Name of a fictional person.'),      message: z.string().describe('Message. Do not use emojis or links.'),    }),  ),});

Client

The client uses useObject to stream the object generation process.

The results are partial and are displayed as they are received. Please note the code for handling undefined values in the JSX.

app/page.tsx

'use client';
import { experimental_useObject as useObject } from '@ai-sdk/react';import { notificationSchema } from './api/notifications/schema';
export default function Page() {  const { object, submit } = useObject({    api: '/api/notifications',    schema: notificationSchema,  });
  return (    <>      <button onClick={() => submit('Messages during finals week.')}>        Generate notifications      </button>
      {object?.notifications?.map((notification, index) => (        <div key={index}>          <p>{notification?.name}</p>          <p>{notification?.message}</p>        </div>      ))}    </>  );}

Server

On the server, we use streamObject to stream the object generation process.

app/api/notifications/route.ts

import { openai } from '@ai-sdk/openai';import { streamObject } from 'ai';import { notificationSchema } from './schema';
// Allow streaming responses up to 30 secondsexport const maxDuration = 30;
export async function POST(req: Request) {  const context = await req.json();
  const result = streamObject({    model: openai('gpt-4.1'),    schema: notificationSchema,    prompt:      `Generate 3 notifications for a messages app in this context:` + context,  });
  return result.toTextStreamResponse();}

Enum Output Mode


When you need to classify or categorize input into predefined options, you can use the enum output mode with useObject. This requires a specific schema structure where the object has enum as a key with z.enum containing your possible values.

Example: Text Classification

This example shows how to build a simple text classifier that categorizes statements as true or false.

Client

When using useObject with enum output mode, your schema must be an object with enum as the key:

app/classify/page.tsx

'use client';
import { experimental_useObject as useObject } from '@ai-sdk/react';import { z } from 'zod';
export default function ClassifyPage() {  const { object, submit, isLoading } = useObject({    api: '/api/classify',    schema: z.object({ enum: z.enum(['true', 'false']) }),  });
  return (    <>      <button onClick={() => submit('The earth is flat')} disabled={isLoading}>        Classify statement      </button>
      {object && <div>Classification: {object.enum}</div>}    </>  );}

Server

On the server, use streamObject with output: 'enum' to stream the classification result:

app/api/classify/route.ts

import { openai } from '@ai-sdk/openai';import { streamObject } from 'ai';
export async function POST(req: Request) {  const context = await req.json();
  const result = streamObject({    model: openai('gpt-4.1'),    output: 'enum',    enum: ['true', 'false'],    prompt: `Classify this statement as true or false: ${context}`,  });
  return result.toTextStreamResponse();}

Customized UI


useObject also provides ways to show loading and error states:

Loading State

The isLoading state returned by the useObject hook can be used for several purposes:

  • To show a loading spinner while the object is generated.
  • To disable the submit button.

app/page.tsx

'use client';
import { useObject } from '@ai-sdk/react';
export default function Page() {  const { isLoading, object, submit } = useObject({    api: '/api/notifications',    schema: notificationSchema,  });
  return (    <>      {isLoading && <Spinner />}
      <button        onClick={() => submit('Messages during finals week.')}        disabled={isLoading}      >        Generate notifications      </button>
      {object?.notifications?.map((notification, index) => (        <div key={index}>          <p>{notification?.name}</p>          <p>{notification?.message}</p>        </div>      ))}    </>  );}

Stop Handler

The stop function can be used to stop the object generation process. This can be useful if the user wants to cancel the request or if the server is taking too long to respond.

app/page.tsx

'use client';
import { useObject } from '@ai-sdk/react';
export default function Page() {  const { isLoading, stop, object, submit } = useObject({    api: '/api/notifications',    schema: notificationSchema,  });
  return (    <>      {isLoading && (        <button type="button" onClick={() => stop()}>          Stop        </button>      )}
      <button onClick={() => submit('Messages during finals week.')}>        Generate notifications      </button>
      {object?.notifications?.map((notification, index) => (        <div key={index}>          <p>{notification?.name}</p>          <p>{notification?.message}</p>        </div>      ))}    </>  );}

Error State

Similarly, the error state reflects the error object thrown during the fetch request. It can be used to display an error message, or to disable the submit button:

We recommend showing a generic error message to the user, such as "Something went wrong." This is a good practice to avoid leaking information from the server.

'use client';
import { useObject } from '@ai-sdk/react';
export default function Page() {  const { error, object, submit } = useObject({    api: '/api/notifications',    schema: notificationSchema,  });
  return (    <>      {error && <div>An error occurred.</div>}
      <button onClick={() => submit('Messages during finals week.')}>        Generate notifications      </button>
      {object?.notifications?.map((notification, index) => (        <div key={index}>          <p>{notification?.name}</p>          <p>{notification?.message}</p>        </div>      ))}    </>  );}

Event Callbacks


useObject provides optional event callbacks that you can use to handle life-cycle events.

  • onFinish: Called when the object generation is completed.
  • onError: Called when an error occurs during the fetch request.

These callbacks can be used to trigger additional actions, such as logging, analytics, or custom UI updates.

app/page.tsx

'use client';
import { experimental_useObject as useObject } from '@ai-sdk/react';import { notificationSchema } from './api/notifications/schema';
export default function Page() {  const { object, submit } = useObject({    api: '/api/notifications',    schema: notificationSchema,    onFinish({ object, error }) {      // typed object, undefined if schema validation fails:      console.log('Object generation completed:', object);
      // error, undefined if schema validation succeeds:      console.log('Schema validation error:', error);    },    onError(error) {      // error during fetch request:      console.error('An error occurred:', error);    },  });
  return (    <div>      <button onClick={() => submit('Messages during finals week.')}>        Generate notifications      </button>
      {object?.notifications?.map((notification, index) => (        <div key={index}>          <p>{notification?.name}</p>          <p>{notification?.message}</p>        </div>      ))}    </div>  );}

Configure Request Options


You can configure the API endpoint, optional headers and credentials using the api, headers and credentials settings.

const { submit, object } = useObject({  api: '/api/use-object',  headers: {    'X-Custom-Header': 'CustomValue',  },  credentials: 'include',  schema: yourSchema,});

On this page

Object Generation

Example

Schema

Client

Server

Enum Output Mode

Example: Text Classification

Client

Server

Customized UI

Loading State

Stop Handler

Error State

Event Callbacks

Configure Request Options

Deploy and Scale AI Apps with Vercel.

Vercel delivers the infrastructure and developer experience you need to ship reliable AI-powered applications at scale.

Trusted by industry leaders:

  • OpenAI
  • Photoroom
  • leonardo-ai Logoleonardo-ai Logo
  • zapier Logozapier Logo

Talk to an expert