Structured Output With the Vercel AI SDK
How to make a model return JSON your code can trust, and why the starter uses generateText with Output.object instead of generateObject.

Free-form text is fine when a human reads the answer. The moment your code reads it, you need a shape you can rely on — and "please reply in JSON" in a system prompt is not a shape, it is a hope.
The pattern
The starter wraps this in lib/ai/structured.ts:
const result = await generateText({
model: resolveModel(modelId),
system,
prompt,
output: Output.object({ schema }),
});
return { object: result.output as T, usage: result.totalUsage };The schema is a Zod object. The SDK turns it into a provider-native structured-output constraint, so the model is not free to invent a different shape, and the response is parsed and validated before you ever touch it.
Describe your fields
The single highest-leverage thing you can do is use .describe() on every field:
z.object({
title: z.string().describe("A short title for the meeting"),
actionItems: z.array(
z.object({
task: z.string().describe("What needs to be done"),
owner: z.string().nullable().describe("Who owns it, or null"),
}),
),
});Those descriptions reach the model. A field called owner with no description invites the model to guess a name; the same field described as "who owns it, or null" gets you a null when nobody was named.
Prefer nullable over optional
An optional field lets the model silently drop information. A nullable field forces it to make a decision you can see. In practice, z.string().nullable() produces far fewer surprises downstream than z.string().optional().
Why not generateObject
generateText with Output.object keeps one code path for both text and JSON responses: the same model resolution, the same usage accounting, the same error handling. The task runner in app/api/tasks/generate/route.ts branches on one field of the template and calls the same layer either way.
You can see the whole thing working in the "Meeting Notes Extractor" template — paste messy notes, get back decisions and action items you can insert straight into a database.
More Articles
Ship an AI SaaS in an Afternoon
A walkthrough of what the starter gives you out of the box, and the handful of decisions left for you to make.
June 2, 2026
Metering AI Usage Without Building a Credit System
Request caps are cruder than token accounting, and for most products they are the right first version. Here is how the starter enforces them.
June 27, 2026