TL;DR:
- The Vercel AI SDK is a model-agnostic TypeScript toolkit that standardises streaming, tool calling, structured output, and multi-step agent patterns across OpenAI, Anthropic, Google, and others
- Its
generateTextwithmaxStepsis one of the cleanest ways to build tool-calling agents in TypeScript without a heavyweight framework - The React hooks (
useChat,useCompletion) make it the natural choice for any Next.js or React AI interface, but the Core layer works independently of any UI framework
If you’re building AI-powered applications in TypeScript or JavaScript, the Vercel AI SDK (the ai package on npm) has quietly become the default choice for a lot of teams — not because it has the most features, but because it has the right abstractions at the right level. It doesn’t try to be an opinionated agent framework. It gives you clean, well-typed building blocks and gets out of the way.
This guide covers the SDK’s structure, the patterns that matter for production agent work, and where it fits in the broader landscape of TypeScript AI tooling.
How the SDK is Organised
The SDK has three layers:
AI SDK Core handles model interactions in a framework-agnostic way. The core functions — generateText, streamText, generateObject, streamObject — work in any TypeScript environment: Node.js, Deno, edge runtimes, or server components. This is where tool calling and agent loops live.
AI SDK UI provides React, Svelte, and Vue hooks that manage streaming state, message history, and server communication. useChat is the main hook — it handles the entire client-server streaming lifecycle and gives you loading states and error handling out of the box.
AI SDK RSC is the React Server Components integration for deeply server-rendered streaming applications. Less commonly needed for agent work specifically.
The model providers are separate packages: @ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/google, etc. Switching providers is genuinely one line of code, which matters when you’re evaluating models or routing between them.
Basic Text Generation and Streaming
import { generateText, streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
// One-shot generation
const { text } = await generateText({
model: anthropic('claude-sonnet-4-6'),
prompt: 'Summarise the key findings from this report: ...',
});
// Streaming
const result = streamText({
model: anthropic('claude-sonnet-4-6'),
prompt: 'Write a project brief for...',
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
Structured Output with generateObject
One of the SDK’s strongest features is generateObject, which uses JSON Schema or Zod schemas to enforce structured output reliably:
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const { object } = await generateObject({
model: openai('gpt-4o'),
schema: z.object({
sentiment: z.enum(['positive', 'negative', 'neutral']),
confidence: z.number().min(0).max(1),
keyTopics: z.array(z.string()).max(5),
summary: z.string().max(200),
}),
prompt: `Analyse the sentiment of this customer review: "${reviewText}"`,
});
// object is fully typed — no JSON.parse, no type assertions
console.log(object.sentiment, object.confidence);
The SDK handles the retry logic when the model produces malformed output, which is what you actually need in production.
Tool Calling and Multi-Step Agents
This is where the SDK shines for agent work. You define tools with Zod schemas for parameters, and the maxSteps parameter enables agentic loops where the model can call tools iteratively until it has a final answer:
import { generateText, tool } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
const result = await generateText({
model: anthropic('claude-sonnet-4-6'),
tools: {
searchDatabase: tool({
description: 'Search the product database for matching items',
parameters: z.object({
query: z.string(),
category: z.enum(['electronics', 'clothing', 'home']).optional(),
maxResults: z.number().default(10),
}),
execute: async ({ query, category, maxResults }) => {
return await db.search({ query, category, limit: maxResults });
},
}),
getProductDetails: tool({
description: 'Get full details for a specific product ID',
parameters: z.object({ productId: z.string() }),
execute: async ({ productId }) => {
return await db.products.findById(productId);
},
}),
},
maxSteps: 5, // allows up to 5 tool call / response cycles
messages: [
{ role: 'user', content: 'Find me wireless headphones under £150 with good noise cancellation' }
],
});
console.log(result.text); // final answer after tool use
console.log(result.steps); // full trace of tool calls and intermediate steps
The result.steps array gives you the complete trace — each tool call, its arguments, and the response — which is essential for debugging and logging in production.
React Integration with useChat
For any Next.js application, useChat handles the streaming client-server cycle:
// app/api/chat/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
messages,
system: 'You are a helpful assistant for our e-commerce platform.',
});
return result.toDataStreamResponse();
}
// components/Chat.tsx
'use client';
import { useChat } from 'ai/react';
export function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/chat',
});
return (
<div>
{messages.map(m => (
<div key={m.id} className={m.role === 'user' ? 'user' : 'assistant'}>
{m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} disabled={isLoading} />
<button type="submit">Send</button>
</form>
</div>
);
}
The hook manages all the streaming state, message history accumulation, and error handling. Tool call results stream in and display incrementally if you wire up the tool rendering.
When to Use the Vercel AI SDK vs Other Frameworks
The SDK is the right choice when:
- You’re building in TypeScript/JavaScript and want excellent types throughout
- You need React/Next.js UI integration with streaming
- You want multi-provider flexibility without vendor lock-in
- You’re building focused agent workflows (tool calling + maxSteps covers most use cases)
Reach for LangGraph or a similar framework instead when you need explicit state machine modelling of agent flow, fan-out/fan-in parallel agent execution, or the kind of human-in-the-loop checkpointing that requires persistent graph state.
The SDK’s maxSteps approach is intentionally simple — the model drives the loop. If you need to encode complex conditional logic about when the agent calls which tools, the imperative control flow of LangGraph gives you more direct control.
Production Considerations
Observability: The SDK integrates with AI gateway providers (LangSmith, Braintrust, Helicone) via the experimental_telemetry option, which passes OpenTelemetry spans to any compatible collector. This gives you latency, token counts, and tool call traces in production without instrumenting the model calls manually.
Rate limiting and retries: The provider packages handle exponential backoff on rate limit errors by default. For agents with maxSteps, set a sensible timeout on the outer generateText call to prevent runaway loops.
Cost awareness: result.usage returns promptTokens, completionTokens, and totalTokens for each call, and result.steps breaks this down per step. Essential for understanding where token cost is coming from in agentic workflows.
The Vercel AI SDK is at its best when you want to build something substantial without fighting your framework. For TypeScript teams building AI features into existing Next.js or Node.js applications, it’s the most direct path from idea to production.