DEV Community

Sangmin Lee
Sangmin Lee

Posted on • Originally published at claudeguide.io

Vercel AI SDK + Claude: Streaming, Tool Use & Edge Deploy

Originally published at claudeguide.io/vercel-ai-sdk-claude

Vercel AI SDK + Claude: Streaming, Tool Use & Edge Deploy (2026)

Install @ai-sdk/anthropic, instantiate the provider with anthropic("claude-sonnet-4-6"), and stream responses with streamText() — Claude works on Vercel AI SDK 5.x out of the box with ~150ms cold start on Edge runtime. The SDK works on both the Edge runtime (SSE-friendly, 25s max execution) and Node (long-running tools, native deps). Tool use, vision, and prompt caching all flow through the same streamText API once you know where to attach providerOptions. Three gotchas catch every team: an outdated anthropic-version header, the system-message role mismatch, and a stream-finalize race that swallows partial output. Below is the working setup, with the exact provider config and a tool-call example you can paste into a Next.js route handler.

Install and minimal example

The SDK is provider-agnostic; you pick the model package separately.

bun add ai @ai-sdk/anthropic
# or: npm i ai @ai-sdk/anthropic
Enter fullscreen mode Exit fullscreen mode

Set ANTHROPIC_API_KEY in .env.local. The provider reads it automatically — no client init needed.

A streaming Next.js route handler:

// app/api/chat/route.ts
import { anthropic } from "@ai-sdk/anthropic";
import { streamText, convertToCoreMessages } from "ai";

export const runtime = "edge"; // or "nodejs"
export const maxDuration = 30;

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = await streamText({
    model: anthropic("claude-sonnet-4-6"),
    system: "You are a concise technical assistant.",
    messages: convertToCoreMessages(messages),
    maxTokens: 1024,
  });

  return result.toDataStreamResponse();
}
Enter fullscreen mode Exit fullscreen mode

Frontend with useChat():

"use client";
import { useChat } from "ai/react";

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat();
  return (
    <form onSubmit={handleSubmit}

If your hit rate stays under 80%, you are almost certainly paying for cache writes you never read. Log `usage.cacheReadInputTokens` vs `usage.cacheCreationInputTokens` on every response and alert when the ratio inverts.

**[Claude API Cost Optimization Masterclass ($59)](https://shoutfirst.gumroad.com/l/msjkda?utm_source=claudeguide&utm_medium=article&utm_campaign=vercel-sdk-mid)** — Includes the exact caching + streaming patterns for Vercel AI SDK deployments, plus a live cost trace from a 50K-call production agent.

## Edge runtime caveats

The Edge runtime is the right default for streaming Claude responses. Cold start is ~150ms in Vercel's measurements, SSE flushing works, and `Response` streaming is native. But the 25-second hard execution limit will kill long tool chains. Switch to `runtime = "nodejs"` when:

- A tool calls a slow external API (DB migration, scraper, render farm).
- You need filesystem access for large attachments.
- You depend on a native module (`sharp`, `canvas`, native crypto).
- You need durable connections (websockets, long polling)  Vercel functions are not the right primitive; use a worker.

`maxDuration = 300` extends Node functions on Pro plans. Hobby plans cap at 60s. If you need 5+ minute jobs, push to a queue and stream the result back via Server-Sent Events from a separate poller.

## Vision: passing image messages

Image input goes in the same `messages` array as multipart content:

Enter fullscreen mode Exit fullscreen mode


ts
const result = await streamText({
model: anthropic("claude-sonnet-4-6"),
messages: [
{
role: "user",
content: [
{ type: "text", text: "What is in this screenshot?" },
{
type: "image",
image: new URL("https://example.com/screenshot.png"),
// or: image: Buffer.from(...) for base64
},
],
},
],
});


The SDK normalizes URL fetching, base64 encoding, and MIME detection. Anthropic accepts JPEG, PNG, GIF, WebP up to ~5MB per image; the SDK does not enforce the size cap, so do it yourself before paying for a rejected request.

## Three specific gotchas

**1. The `anthropic-version` header default may be outdated.** The pinned version inside `@ai-sdk/anthropic` lags behind the API. If you need a recent feature (extended thinking, files API, the latest tool-use mode), override it:

Enter fullscreen mode Exit fullscreen mode


ts
const model = anthropic("claude-sonnet-4-6", {
headers: { "anthropic-version": "2026-04-01" },
});




Or globally via `providerOptions.anthropic.headers` on the call. Symptom: a feature works in the raw SDK but silently no-ops through Vercel AI SDK.

**2. The SDK normalizes `assistant` and `user` roles but NOT `system`.** Anthropic's API takes `system` as a top-level parameter, not a message. If you put a `{ role: "system" }` entry in `messages`, behavior depends on SDK version: some swallow it, some convert it, some throw. Always pass system text via the top-level `system:` field on `streamText`.

**3. Stream finalize race — check `finishReason` on every chunk.** A stream that ends cleanly has `finishReason: "stop"` or `"tool-calls"`. A stream that ends with `"length"` was truncated by `maxTokens`; `"content-filter"` means a safety block. If you only render `text` and never inspect `finishReason`, your UI silently shows half-answers when limits hit. Wire it through `useChat`'s `onFinish` callback or read `result.finishReason` on the server.

## Production deploy

There is almost no config:

1. `ANTHROPIC_API_KEY` in Vercel project env vars (Production + Preview).
2. `runtime` and `maxDuration` exported from the route handler.
3. Vercel Firewall to rate-limit unauthenticated `/api/chat` calls (Claude requests cost real money — never expose an unprotected endpoint).
4. Cost guardrails — the [Claude API cost monitoring guide](/claude-api-cost-monitoring-guide) shows how to attach per-user spend caps via Anthropic's usage API.

That is it. No region pinning, no warmup, no provider-specific build flags.

## Vercel AI SDK vs raw `@anthropic-ai/sdk`

Skip the Vercel SDK when you need:

- **Batch API** (50% discount on async jobs 

## Frequently Asked Questions

### Does it support prompt caching?

Yes, via `providerOptions.anthropic.cacheControl: { type: "ephemeral" }` on the message you want cached. The SDK does not auto-detect cacheable prefixes — you mark them. Verify hits by logging `usage.cacheReadInputTokens` from the `onFinish` callback.

### Edge or Node runtime?

Default to Edge for chat-style apps: faster cold start, native SSE, lower cost on Vercel. Switch to Node when a tool runs longer than 25 seconds, you need a native module, or you call the filesystem. The runtime is per-route — mix freely in the same app.

### Can I switch between Claude and OpenAI dynamically?

Yes. Both providers implement the same `LanguageModelV1` interface, so `streamText({ model: useClaude ? anthropic(...) : openai(...) })` works. Tool definitions are portable; vision message format is identical. The one wart: provider-specific options (cache control, reasoning effort) are siloed under `providerOptions.anthropic` vs `providerOptions.openai`.

### Does useChat work with tool calls?

Yes, with `maxSteps` set on the server and tool-call rendering wired in your message component. The hook exposes `message.toolInvocations` — iterate it to render tool inputs/outputs. Without `maxSteps > 1` the loop stops after the first tool result and Claude never gets to summarize.

### Why is my stream cut off?

Three causes, in order of likelihood: (1) `maxTokens` hit — check `finishReason === "length"`. (2) Edge runtime 25-second timeout — switch to Node or shorten the work. (3) Client disconnected and the SDK aborted — `useChat` handles this, but custom fetchers may need an `AbortController` wired through. Always log `finishReason` server-side; "silent truncation" is almost always one of these three.

---

*Last verified May 2026 against `@ai-sdk/anthropic` v1.x and `ai` v5.x. Model IDs and runtime caps refresh quarterly — check the [Vercel AI SDK changelog](https://sdk.vercel.ai/docs) before pinning to a version.*
Enter fullscreen mode Exit fullscreen mode

Top comments (0)