useObject streams a partial JSON object as it generates, which is perfect for dashboards that fill in live. The schema must be identical on both sides, so keep it in one shared file.
Shared schema (app/api/notifications/schema.ts):
import { z } from 'zod';
export const notificationSchema = z.object({
notifications: z.array(z.object({
name: z.string().describe('Name of a fictional person.'),
message: z.string().describe('Message text.'),
})),
});
Client ('use client'):
import { useObject } from '@ai-sdk/react';
import { notificationSchema } from './api/notifications/schema';
const { object, submit, isLoading } = useObject({
api: '/api/notifications',
schema: notificationSchema,
});
// submit('some prompt') starts generation; object is partial until done
Server:
import { Output, streamText } from 'ai';
import { notificationSchema } from './schema';
export const maxDuration = 30;
export async function POST(req: Request) {
const { prompt } = await req.json();
const result = streamText({
model: 'your-model-id',
output: Output.object({ schema: notificationSchema }),
prompt,
});
return result.toTextStreamResponse();
}
Checklist:
1. In v6+, generateObject/streamObject are deprecated. The replacement is streamText/generateText with output: Output.object({ schema }). Do not copy old generateObject snippets.
2. useObject exists for React, Svelte, and Vue. Import it from @ai-sdk/react (or the matching framework package), not from ai.
3. The streamed object is partial: every field is undefined until its tokens arrive. Guard every access (object?.notifications?.map) or the UI crashes on first render.
4. Add .describe() to schema fields. The descriptions become part of the model prompt and noticeably improve field quality.
5. Zod 4.1.8 or later is required with AI SDK 5+. Older zod versions cause TypeScript performance meltdowns in big schemas.
6. If the model wraps JSON in markdown fences, add extractJsonMiddleware via wrapLanguageModel on the server model.