The Vercel AI SDK is a TypeScript library for building AI-powered applications. It provides a unified interface for calling language models, streaming their responses, using tools, and generating structured output. You write one set of functions. Swap providers by changing a single import.
The SDK is split into two packages. AI SDK Core (ai) handles server-side model calls, tool execution, and structured generation. AI SDK UI (@ai-sdk/react, @ai-sdk/svelte, @ai-sdk/vue) provides frontend hooks for chat interfaces, completions, and streaming state management.
The library is framework-agnostic on the server side, but it works best with Next.js App Router. Server actions, route handlers, and React Server Components all integrate cleanly.
The simplest way to call a model and stream the response:
import { streamText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
const result = streamText({
model: anthropic("claude-sonnet-4-20250514"),
prompt: "Explain TypeScript generics in two sentences.",
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
That is all it takes. streamText returns a StreamTextResult with a textStream async iterable. Each chunk arrives as the model generates it. No manual SSE parsing. No ReadableStream wiring.
For a Next.js route handler, return the stream directly:
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,
});
return result.toDataStreamResponse();
}
On the frontend, the useChat hook handles everything:
"use client";
import { useChat } from "@ai-sdk/react";
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat();
return (
<div>
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
</form>
</div>
);
}
The hook manages message history, loading state, error handling, and abort control. It connects to your /api/chat route handler automatically.
Tools let the model call functions you define. The SDK handles the full loop: the model decides to call a tool, your function executes, and the result feeds back into the conversation.
import { streamText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
const result = streamText({
model: anthropic("claude-sonnet-4-20250514"),
prompt: "What is the weather in San Francisco?",
tools: {
getWeather: tool({
description: "Get the current weather for a location",
parameters: z.object({
city: z.string().describe("The city name"),
}),
execute: async ({ city }) => {
// Call your weather API here
return { temperature: 62, condition: "Foggy", city };
},
}),
},
maxSteps: 5,
});
The parameters field uses Zod schemas. The SDK converts these to JSON Schema for the model and validates the response before calling execute. Type safety flows from the schema definition through to the function arguments.
maxSteps controls how many tool-call/result rounds the model can perform before returning. Set it to 1 for single-shot tool use, or higher for multi-step reasoning where the model chains multiple tool calls together.
Tools work with streaming too. The useChat hook on the frontend renders tool invocations and results as part of the message stream, so you can show real-time progress as tools execute.
Get the weekly deep dive
Tutorials on Claude Code, AI agents, and dev tools - delivered free every week.
Sometimes you want the model to return data, not prose. generateObject enforces a Zod schema on the output:
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({
name: z.string(),
ingredients: z.array(z.string()),
prepTimeMinutes: z.number(),
steps: z.array(z.string()),
}),
prompt: "Generate a recipe for chocolate chip cookies.",
});
console.log(object.name);
// "Classic Chocolate Chip Cookies"
console.log(object.ingredients);
// ["2 1/4 cups flour", "1 tsp baking soda", ...]
The return type is fully typed. object.name is a string, object.ingredients is string[]. No casting, no runtime checks. If the model returns something that does not match the schema, the SDK retries automatically.
There is also streamObject for streaming structured data as it generates:
import { streamObject } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
const result = streamObject({
model: anthropic("claude-sonnet-4-20250514"),
schema: z.object({
summary: z.string(),
keyPoints: z.array(z.string()),
sentiment: z.enum(["positive", "negative", "neutral"]),
}),
prompt: "Analyze this customer review: ...",
});
for await (const partial of result.partialObjectStream) {
console.log(partial);
// { summary: "The cust..." }
// { summary: "The customer enjoyed...", keyPoints: ["Fast shipping"] }
// ...progressively more complete
}
Each iteration yields a partial object that grows as the model generates more tokens. This is powerful for UIs where you want to show fields as they appear.
The SDK supports every major provider through a consistent interface. Install the provider package, import it, and pass the model to any function:
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { openai } from "@ai-sdk/openai";
import { google } from "@ai-sdk/google";
import { mistral } from "@ai-sdk/mistral";
// Same function signature, different providers
const claudeResult = await generateText({
model: anthropic("claude-sonnet-4-20250514"),
prompt: "Hello from Claude",
});
const gptResult = await generateText({
model: openai("gpt-4o"),
prompt: "Hello from GPT",
});
const geminiResult = await generateText({
model: google("gemini-2.5-pro"),
prompt: "Hello from Gemini",
});
const mistralResult = await generateText({
model: mistral("mistral-large-latest"),
prompt: "Hello from Mistral",
});
Every provider supports the same core functions: generateText, streamText, generateObject, streamObject. Tools and structured output work across all of them. The model interface is standardized, so switching providers is a one-line change.
For open source models, use the OpenAI-compatible provider pointed at your inference server:
import { createOpenAI } from "@ai-sdk/openai";
const ollama = createOpenAI({
baseURL: "http://localhost:11434/v1",
apiKey: "ollama",
});
const result = await generateText({
model: ollama("llama3.1"),
prompt: "Running locally with Ollama",
});
This works with Ollama, vLLM, LM Studio, or any OpenAI-compatible endpoint. Your application code stays identical regardless of whether the model runs in the cloud or on your machine.
Here is a complete Next.js route handler that combines streaming, tools, and multi-step reasoning:
import { streamText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: anthropic("claude-sonnet-4-20250514"),
system: "You are a helpful coding assistant. Use tools when needed.",
messages,
tools: {
searchDocs: tool({
description: "Search documentation for a framework or library",
parameters: z.object({
query: z.string().describe("The search query"),
framework: z.string().describe("The framework name"),
}),
execute: async ({ query, framework }) => {
// Your search implementation
return { results: [`${framework}: ${query} - found 3 matches`] };
},
}),
runCode: tool({
description: "Execute a TypeScript code snippet",
parameters: z.object({
code: z.string().describe("TypeScript code to execute"),
}),
execute: async ({ code }) => {
// Your sandbox execution
return { output: "Executed successfully", code };
},
}),
},
maxSteps: 10,
});
return result.toDataStreamResponse();
}
The model can search documentation, run code, and chain those operations together across multiple steps. The frontend receives a single stream with text, tool calls, and tool results interleaved. The useChat hook handles all of it.
The AI SDK is TypeScript-first in a way that actually changes how you build. Zod schemas for tools and structured output mean your AI inputs and outputs have the same type guarantees as the rest of your application. Refactor a tool's parameters and TypeScript catches every call site. Change a structured output schema and the compiler tells you where the UI needs to update.
This is the direction AI application development is heading. Not string templates and JSON parsing, but typed interfaces with compile-time safety.
Install the SDK and a provider:
npm install ai @ai-sdk/anthropic @ai-sdk/openai zodSet your API key:
export ANTHROPIC_API_KEY="your-key"
Run the three-line streaming example from earlier. Then add useChat on the frontend. Then add a tool. Each step builds on the last, and the SDK handles the complexity underneath.
For a deeper look at AI frameworks and how the AI SDK compares, check out the frameworks overview on SubAgent.
Technical content at the intersection of AI and development. Building with AI agents, Claude Code, and modern dev tools - then showing you exactly how it works.
The TypeScript toolkit for building AI apps. Unified API across OpenAI, Anthropic, Google. Streaming, tool calling, stru...
View ToolStackBlitz's in-browser AI app builder. Full-stack apps from a prompt - runs Node.js, installs packages, and deploys....
View ToolNew tutorials, open-source projects, and deep dives on coding agents - delivered weekly.
Deployment platform behind Next.js. Git push to deploy. Edge functions, image optimization, analytics. Free tier is gene...

Repo: https://git.new/ai-pin Building an AI Assistant similar to the Humane AI Pin, the Rabbit R1 with Advanced Functionality from Scratch This video details the process of creating an AI...

Building a Perplexity Style LLM Answer Engine: Frontend to Backend Tutorial This tutorial guides viewers through the process of building a Perplexity style Large Language Model (LLM) answer...

In this video I will show you how to get up and running in just a few minutes with getting a Next.js chatbot that leverages the brand new vercel ai sdk as well as the OpenAI SDK. Huge shout...
AI agents use LLMs to complete multi-step tasks autonomously. Here is how they work and how to build them in TypeScript.
A practical guide to building Next.js apps using Claude Code, Cursor, and the modern TypeScript AI stack.
Two popular frameworks for building AI apps in TypeScript. Here is when to use each and why most Next.js developers shou...