Vercel AI SDK Best Practices
Ten practices for streaming chat, generative tool UI, approvals, and edge-friendly deploy of full-stack web agents with the Vercel AI SDK.
Search across all documentation pages
Ten practices for streaming chat, generative tool UI, approvals, and edge-friendly deploy of full-stack web agents with the Vercel AI SDK.
Use this list when reviewing a Next.js agent PR or hardening a chat route before production.
message.parts, not a single content string. Text, tools, data, and approvals all travel as parts; content-only UIs hide generative UI.convertToModelMessages (or current helper) before streamText; never send raw UI metadata as if it were provider format.stopWhen / step caps and maxDuration so tools cannot run until the platform kills the function.status and errors. Disable send while submitted/streaming, offer Stop, and show a generic Retry path without leaking server details.description + Zod inputSchema; map tool-${name} part states to components (loading, result, error).execute for privileged tools; client tools only for device/UI concerns with addToolOutput.needsApproval / toolApproval) plus addToolApprovalResponse; re-validate policy on the second pass./api/chat. Authorize from session cookies or verified tokens, not a user id field in the JSON body.sdk_version with @ai-sdk/react.| Stage | Habits | Exit criterion |
|---|---|---|
| Prototype | 1-4 | Streaming chat works with parts + status |
| Productize | 5-7 | Tools render; writes need approval |
| Secure | 8-9 | Authn/z + untrusted history model |
| Operate | 10-12 | Runtime, versions, and traces production-ready |
handleSubmit tutorials from old majors mixed with AI SDK 6 transports without reading migration notes.refund / delete / shell tools with no approval.// Pattern: edge-safe stream + tool + step cap (verify APIs at build)
import {
convertToModelMessages,
isStepCount,
streamText,
tool,
type UIMessage,
} from "ai";
import { z } from "zod";
export const runtime = "edge";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
// authenticate(req) here
const result = streamText({
model: "openai/gpt-4o-mini",
messages: await convertToModelMessages(messages),
stopWhen: isStepCount(5),
tools: {
getStatus: tool({
description: "Return a simple status payload for UI",
inputSchema: z.object({ label: z.string() }),
execute: async ({ label }) => ({ label, ok: true }),
}),
},
});
return result.toUIMessageStreamResponse();
}Parts-based rendering (1), loop/duration bounds (3), and server-side trust/auth for tools (8-9).
No. Read-only demos can skip it. Any tool that writes, spends, or exfiltrates needs a gate.
No. Prefer Edge for simple streaming chat with fetch-based providers. Prefer Node for heavy agents.
On every AI SDK major bump, after a production agent incident, and when adding a new write tool.
Yes. SameChat transport + Core patterns work on other hosts; Vercel Edge specifics apply only on Vercel/Next.
Use chains/routing/evaluator-optimizer when free tool loops are too unreliable; still apply bounds, logging, and auth.
Parts rendering, stopWhen/maxDuration, no browser secrets, approval on write tools, and a note on Edge vs Node choice.
Contract-test the route with mocked model streams when possible; smoke-test previews for real provider streams.
Assuming v4/v5 chat APIs (api option, input from the hook, content rendering) still match AI SDK 6 transports and parts.
UI habits still apply; add protocol version alignment and a single streaming proxy (habit 11).
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.
Reviewed by Chris St. John·Last updated Jul 16, 2026