Vercel AI SDK
The Vercel AI SDK is the most-used TypeScript LLM toolkit — a vendor-neutral abstraction over OpenAI, Anthropic, Google, xAI, Mistral, Cohere, Groq, Together AI, Fireworks, DeepInfra, Replicate, Amazon Bedrock, Azure OpenAI, Google Vertex, Ollama, and OpenRouter, plus React/Vue/Svelte/Solid hooks for streaming chat UIs. It ships in three layers — Core (functions you call from server code), UI (framework hooks for the client), and RSC (React Server Component utilities for streaming UI components from the model) — and pairs with Vercel AI Gateway (Vercel-hosted router with caching, fallbacks, and analytics across providers). Designed for Vercel Functions but works anywhere — the SDK has no Vercel dependencies.
See also
- vercel-edge-network-and-functions — Edge + streaming runtime is the canonical AI SDK deployment
- claude-api-and-sdks — Anthropic’s native SDK (AI SDK wraps this)
- bedrock-api-and-converse — Bedrock as an AI SDK provider
- vercel-observability-and-monitoring — AI Gateway observability
1. Install
pnpm i ai # core SDK
pnpm i @ai-sdk/anthropic # provider plugin
pnpm i @ai-sdk/openai # another provider plugin
pnpm i zod # for tool/object schemasThe unified model string syntax (e.g. "anthropic/claude-sonnet-4-6") requires the relevant provider plugin to be installed.
2. Core — server-side functions
Per sdk.vercel.ai/docs/ai-sdk-core. Four primary functions:
| Function | Purpose | Returns |
|---|---|---|
generateText | Single completion, full response | { text, toolCalls, finishReason, usage } |
streamText | Streaming completion | Stream object with .toDataStreamResponse(), .textStream, .fullStream |
generateObject | Type-safe structured output | { object, finishReason, usage } |
streamObject | Streaming structured output | Stream of partial objects |
embed / embedMany | Text embedding | { embedding, usage } |
generateImage | Image generation | { image, finishReason } |
2.1 generateText
import { generateText } from "ai";
const { text, usage, finishReason } = await generateText({
model: "anthropic/claude-sonnet-4-6",
prompt: "Explain CAP theorem in 50 words.",
});
console.log(text);
console.log(`Tokens: ${usage.totalTokens}, stop: ${finishReason}`);Switch providers by changing the model string — same generateText call works across providers:
// Anthropic
await generateText({ model: "anthropic/claude-opus-4-7", prompt: "..." });
// OpenAI
await generateText({ model: "openai/gpt-5.2", prompt: "..." });
// Google
await generateText({ model: "google/gemini-3.0-pro", prompt: "..." });
// xAI
await generateText({ model: "xai/grok-4", prompt: "..." });
// Local via Ollama
await generateText({ model: "ollama/llama3.3", prompt: "..." });The unified model string requires the provider package installed. The string syntax was added in AI SDK 5 (mid-2025); older code uses explicit provider imports:
import { anthropic } from "@ai-sdk/anthropic";
const { text } = await generateText({
model: anthropic("claude-sonnet-4-6"),
prompt: "...",
});2.2 streamText
The bread-and-butter API for AI chat UIs:
import { streamText } from "ai";
const result = streamText({
model: "anthropic/claude-sonnet-4-6",
messages: [
{ role: "system", content: "You are a coding tutor." },
{ role: "user", content: "Explain async/await." },
],
maxTokens: 1024,
temperature: 0.3,
});
// Server (in a Vercel Function):
return result.toDataStreamResponse();
// Or consume tokens server-side:
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}2.3 generateObject — type-safe structured output
Pair with Zod for end-to-end type safety:
import { generateObject } from "ai";
import { z } from "zod";
const recipeSchema = z.object({
name: z.string(),
prep_minutes: z.number(),
ingredients: z.array(z.object({
name: z.string(),
amount: z.string(),
})),
steps: z.array(z.string()),
});
const { object } = await generateObject({
model: "anthropic/claude-sonnet-4-6",
schema: recipeSchema,
prompt: "Generate a lasagna recipe.",
});
// `object` is typed as inferred Zod schema
console.log(object.name); // string
console.log(object.ingredients[0].name); // stringUnder the hood: the SDK uses the model’s native structured-output mode (JSON schema mode on OpenAI, tool-use-as-schema on Anthropic, function-calling on others). Falls back to prompted JSON if the model doesn’t support it.
2.4 streamObject — streaming structured output
Useful for showing incremental progress on long structured generations:
const { partialObjectStream } = streamObject({
model: "anthropic/claude-sonnet-4-6",
schema: recipeSchema,
prompt: "Generate a complex 50-step recipe.",
});
for await (const partial of partialObjectStream) {
// `partial` is the partially-constructed object as it streams in
console.log(JSON.stringify(partial, null, 2));
}2.5 Tool use
Define tools with Zod schemas + an execute function:
import { generateText, tool } from "ai";
import { z } from "zod";
const { text, toolCalls, toolResults } = await generateText({
model: "anthropic/claude-sonnet-4-6",
prompt: "What's the weather in Paris and London?",
tools: {
getWeather: tool({
description: "Get the current weather for a city.",
inputSchema: z.object({
city: z.string().describe("City name"),
unit: z.enum(["C", "F"]).default("C"),
}),
execute: async ({ city, unit }) => {
const data = await fetchWeather(city, unit);
return { temperature: data.temp, conditions: data.conditions };
},
}),
},
// Run multi-step tool-use loop until model returns a final answer
maxSteps: 5,
});maxSteps controls the tool-calling loop — model calls tool, SDK executes, returns result, model continues. With Claude/OpenAI/etc. supporting parallel tool use, multiple tools can fire per step.
2.6 Embeddings
import { embed, embedMany } from "ai";
const { embedding } = await embed({
model: "openai/text-embedding-3-small",
value: "What is CAP theorem?",
});
const { embeddings } = await embedMany({
model: "openai/text-embedding-3-small",
values: ["text 1", "text 2", "text 3"],
});3. UI — framework hooks for chat UIs
Per sdk.vercel.ai/docs/ai-sdk-ui. Three primary hooks (React shown; same exists for Vue, Svelte, Solid):
| Hook | Purpose |
|---|---|
useChat | Multi-turn conversation with auto-streaming |
useCompletion | Single completion (form-style) |
useObject | Streaming structured output |
3.1 useChat
"use client";
import { useChat } from "@ai-sdk/react";
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading, error } = useChat({
api: "/api/chat",
initialMessages: [{ id: "0", role: "system", content: "..." }],
});
return (
<div>
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
placeholder="Say something..."
disabled={isLoading}
/>
</form>
{error && <div>Error: {error.message}</div>}
</div>
);
}Paired with a server route:
// app/api/chat/route.ts
import { streamText } from "ai";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: "anthropic/claude-sonnet-4-6",
messages,
});
return result.toDataStreamResponse();
}
export const runtime = "edge";The wire format between client and server is automatically managed by toDataStreamResponse ⇄ useChat.
3.2 useCompletion
Single-shot completion:
const { completion, input, handleInputChange, handleSubmit, isLoading } = useCompletion({
api: "/api/completion",
});3.3 useObject
For streaming structured output to the UI:
const { object, submit, isLoading } = useObject({
api: "/api/recipe",
schema: recipeSchema,
});
// object is the partial schema-typed object as it streams in4. RSC — React Server Components AI elements
Per sdk.vercel.ai/docs/ai-sdk-rsc. The ai/rsc module lets server code stream React components, not just text:
// app/actions.ts (server action)
"use server";
import { streamUI } from "ai/rsc";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
export async function submit(message: string) {
const result = await streamUI({
model: anthropic("claude-sonnet-4-6"),
messages: [{ role: "user", content: message }],
text: ({ content }) => <p>{content}</p>,
tools: {
showWeather: {
description: "Display weather UI",
parameters: z.object({ city: z.string() }),
generate: async function* ({ city }) {
yield <div>Loading weather for {city}...</div>;
const data = await fetchWeather(city);
return <WeatherCard data={data} />;
},
},
},
});
return result.value; // a React server component tree
}Used in conjunction with useUIState and useActions for managing the UI on the client side. Powerful for AI apps that render rich UI (cards, charts, forms) generated by the model.
5. Provider plugins — the catalog
Per sdk.vercel.ai/docs/foundations/providers-and-models. Each provider has a package + a unified-string syntax:
| Provider | Package | String prefix | Notes |
|---|---|---|---|
| Anthropic | @ai-sdk/anthropic | anthropic/ | Native Claude models |
| OpenAI | @ai-sdk/openai | openai/ | GPT-5.x, GPT-4.x, embeddings, image gen |
| Google Generative AI | @ai-sdk/google | google/ | Gemini |
| Google Vertex | @ai-sdk/google-vertex | google-vertex/ | Same Gemini via Vertex auth |
| xAI | @ai-sdk/xai | xai/ | Grok |
| Mistral | @ai-sdk/mistral | mistral/ | Mistral-hosted |
| Cohere | @ai-sdk/cohere | cohere/ | Cohere-hosted |
| Groq | @ai-sdk/groq | groq/ | Open-weight models on LPUs — fastest inference |
| Together AI | @ai-sdk/togetherai | togetherai/ | Open-weight catalog |
| Fireworks | @ai-sdk/fireworks | fireworks/ | Open-weight catalog |
| DeepInfra | @ai-sdk/deepinfra | deepinfra/ | Open-weight catalog |
| Replicate | @ai-sdk/replicate | replicate/ | Open-weight + image/video models |
| Amazon Bedrock | @ai-sdk/amazon-bedrock | amazon-bedrock/ | Claude, Llama, Nova on AWS |
| Azure OpenAI | @ai-sdk/azure | azure/ | OpenAI on Azure |
| Ollama | ollama-ai-provider (community) | ollama/ | Local models |
| OpenRouter | @openrouter/ai-sdk-provider (community) | openrouter/ | 200+ models behind one API |
All providers expose the same surface — generateText, streamText, etc., where supported. Some advanced features (extended thinking, prompt caching, computer use) require provider-specific options:
import { anthropic } from "@ai-sdk/anthropic";
const { text } = await generateText({
model: anthropic("claude-sonnet-4-6"),
prompt: "Solve this problem step by step.",
providerOptions: {
anthropic: {
thinking: { type: "enabled", budgetTokens: 4096 },
cacheControl: { type: "ephemeral" },
},
},
});6. AI Gateway
Per vercel.com/docs/ai-gateway. Vercel-hosted router that sits between your code and the provider APIs:
// Without AI Gateway
const result = streamText({
model: anthropic("claude-sonnet-4-6"),
...
});
// With AI Gateway
const result = streamText({
model: "anthropic/claude-sonnet-4-6", // ← gateway intercepts
...
});Features:
- Cross-provider auth — set provider keys once in the Vercel dashboard; AI Gateway uses them. No need to ship keys to your functions.
- Caching — automatic for deterministic requests (temperature 0).
- Rate limiting — per-team or per-project.
- Fallback chain — declare priority order across providers. If Anthropic rate-limits, fall back to OpenAI.
- Analytics — per-provider, per-model spend + latency + token usage in Vercel Observability.
Pricing: $0 (passes through provider costs). Vercel makes money on the surrounding usage.
Fallback example:
const result = streamText({
model: "anthropic/claude-sonnet-4-6",
fallbackModels: [
"openai/gpt-5.2",
"google/gemini-3.0-pro",
],
...
});7. Stop conditions
Per sdk.vercel.ai/docs/ai-sdk-core/tool-calling. Control when streaming stops:
import { streamText, stepCountIs, hasToolCall } from "ai";
const result = streamText({
model: "...",
messages,
tools: { ... },
stopWhen: [
stepCountIs(10), // stop after 10 tool-use steps
hasToolCall("finalAnswer"), // stop when the model calls this tool
],
});8. Error handling
import { generateText, AISDKError, APICallError, NoSuchToolError } from "ai";
try {
const result = await generateText({ ... });
} catch (err) {
if (APICallError.isInstance(err)) {
console.log(`API error ${err.statusCode}: ${err.responseBody}`);
} else if (NoSuchToolError.isInstance(err)) {
console.log(`Model called unknown tool: ${err.toolName}`);
} else if (AISDKError.isInstance(err)) {
console.log(`SDK error: ${err.message}`);
} else {
throw err;
}
}9. Telemetry — OpenTelemetry
The SDK emits OTel spans for every model call. Configure once:
import { generateText } from "ai";
const { text } = await generateText({
model: "...",
prompt: "...",
experimental_telemetry: {
isEnabled: true,
functionId: "summarize-doc", // appears in traces
metadata: { userId: "u_7281", env: "prod" },
},
});In Vercel, these spans flow into Observability automatically. Outside Vercel, configure an OTel exporter (e.g. Sentry, Datadog) and the SDK’s spans show up there.
10. Vercel-specific deployment patterns
10.1 Edge runtime + streaming
// app/api/chat/route.ts
export const runtime = "edge"; // V8 isolate
export const maxDuration = 25; // streaming max on Edge
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: "anthropic/claude-sonnet-4-6",
messages,
});
return result.toDataStreamResponse();
}10.2 Node.js runtime + Fluid Compute (for heavier processing)
// app/api/agent/route.ts
export const runtime = "nodejs"; // Node 22 — supports more deps
export const maxDuration = 300; // 5min for long agent loops
export async function POST(req: Request) {
// Multi-step agent with tools that hit DBs
const { text } = await generateText({
model: "anthropic/claude-sonnet-4-6",
messages: ...,
tools: { ... }, // tools using Node-specific libs
maxSteps: 20,
});
return Response.json({ result: text });
}11. AI SDK version history (high-level)
- v3 (2024) — original release, function-per-provider API.
- v4 (mid-2025) — unified model strings, AI Gateway integration,
streamObjectGA. - v5 (early 2026) — RSC GA, computer-use tool integration, MCP client support, stop conditions API.