Pick TanStack AI for a new app. Stay on the Vercel AI SDK if your app already speaks its UI Message Stream protocol.
Both are open-source TypeScript toolkits. Both stream chat, call tools, talk to many providers, and run on any host. TanStack AI streams the open AG-UI protocol. The Vercel AI SDK streams its own protocol.
TanStack AI also does the hard parts. Each one is its own package, so you load only what the task needs:
Devtools show each tool call, interrupt, memory recall, and finish reason, on the server and in the client, in one timeline. See Devtools.
Your app already speaks the AI SDK stream protocol. For example, your UI uses @ai-sdk/react or AI Elements, or a backend that you do not control emits the UI Message Stream. TanStack AI does not read or write that protocol.
Vercel AI Gateway is not a reason to stay. TanStack AI has a native Vercel AI Gateway adapter for chat, embeddings, and images.
npm i @tanstack/ai @tanstack/ai-react @tanstack/ai-openaipnpm add @tanstack/ai @tanstack/ai-react @tanstack/ai-openaiyarn add @tanstack/ai @tanstack/ai-react @tanstack/ai-openaibun add @tanstack/ai @tanstack/ai-react @tanstack/ai-openaiPut OPENAI_API_KEY in the environment of your server.
Add a server route. This is the TanStack AI version of streamText(...).toUIMessageStreamResponse():
import {
chat,
chatParamsFromRequest,
toServerSentEventsResponse,
} from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
export async function POST(request: Request) {
const { messages, threadId, runId } = await chatParamsFromRequest(request)
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages,
threadId,
runId,
})
return toServerSentEventsResponse(stream)
}import { useState } from 'react'
import { fetchServerSentEvents, useChat } from '@tanstack/ai-react'
export function Chat() {
const [input, setInput] = useState('')
const { messages, sendMessage } = useChat({
connection: fetchServerSentEvents('/api/chat'),
threadId: 'support-chat',
})
return (
<>
{messages.map((message) => (
<div key={message.id}>
{message.parts.map((part, index) =>
part.type === 'text' ? <p key={index}>{part.content}</p> : null,
)}
</div>
))}
<form
onSubmit={(event) => {
event.preventDefault()
sendMessage(input)
setInput('')
}}
>
<input value={input} onChange={(event) => setInput(event.target.value)} />
<button type="submit">Send</button>
</form>
</>
)
}Send a message. The reply streams into the page. Other frameworks are in the Quick Start.
| Vercel AI SDK | TanStack AI |
|---|---|
| ai | @tanstack/ai |
| @ai-sdk/openai and openai('model') | @tanstack/ai-openai and openaiText('model') |
| streamText() | chat() |
| generateText() | chat({ stream: false }) |
| generateObject() / streamObject() / Output.object() | chat({ outputSchema }) |
| system: '...' | systemPrompts: ['...'] |
| providerOptions: { openai: { ... } } | modelOptions: { ... } (typed per model) |
| tool({ execute }) | toolDefinition({ ... }).server(fn) or .client(fn) |
| stopWhen: stepCountIs(n) | agentLoopStrategy: maxIterations(n) |
| wrapLanguageModel(), onFinish, prepareStep | middleware: [...] with onFinish, onConfig, and more |
| result.toUIMessageStreamResponse() | toServerSentEventsResponse(stream) |
| useChat from @ai-sdk/react | useChat from @tanstack/ai-react |
Every option, result member, and hook difference is in Migration from Vercel AI SDK.
Versions: Vercel AI SDK ai@7.x and TanStack AI 1.0.
| Feature | Vercel AI SDK | TanStack AI |
|---|---|---|
| License | Apache 2.0 | MIT |
| Hosting | Works anywhere. Optional Vercel platform parts | Works anywhere. No platform layer |
| Wire protocol | Own UI Message Stream. AG-UI only through the external @ag-ui/vercel-ai-sdk bridge | Native AG-UI, in both directions |
| Providers | About 38 first-party provider packages, plus community packages | OpenAI, Anthropic, Gemini, Vertex, Bedrock, Grok, Groq, Mistral, Cohere, Ollama, OpenRouter, BytePlus, ElevenLabs, fal, Cloudflare Workers AI, and more. Any OpenAI-compatible endpoint through openaiCompatible |
| Gateways | Vercel AI Gateway (the default provider) | Vercel AI Gateway, Cloudflare AI Gateway, OpenRouter, LLM Gateway, Lovable AI Gateway |
| Framework packages | React, Vue, Svelte, Angular. Solid is community-maintained | React, Solid, Vue, Svelte, Preact, Angular, Octane, Remix, React Native |
| UI hooks | useChat, useCompletion, useObject | One hook per activity: chat, structured output, image, video, audio, speech, transcription, summarize, realtime |
| Type safety | Per provider | Per model |
| Bundle | One package per provider | One adapter per activity, so you bundle only what you import |
| Tools | tool() on the server. Client tools through onToolCall | One toolDefinition(), with .server() and .client() |
| Tool approval | Per-tool needsApproval | Per-tool needsApproval, with batched approvals and interrupts |
| Lazy tool discovery | Anthropic tool search only (deferLoading) | Any provider, in your own agent loop |
| Agent loop | stopWhen conditions and the ToolLoopAgent class | Strategy functions (state) => boolean. Share the config with createChatOptions |
| Middleware | wrapLanguageModel() around the model | Named hooks across the whole run: config, chunks, tool calls, usage, finish |
| Structured output | Per call (Output, generateObject(), streamObject()) | Typed message part, streamed with tools and kept in the history |
| Chat persistence | A pattern: you write saveChat and loadChat | withPersistence middleware, server or browser stores |
| Resumable streams | Redis, the resumable-stream package, and routes you write | Built in. useChat reconnects by itself |
| Memory | Third-party memory providers or a custom tool | memoryMiddleware with Redis, mem0, Honcho, Hindsight, or your own |
| Compaction | - | @tanstack/ai-compaction middleware |
| MCP client | @ai-sdk/mcp (stable) | @tanstack/ai-mcp: multi-server pools, generated types, lazy tools, managed lifecycle |
| MCP Apps | experimental_MCPAppRenderer (React only) | React, Preact, and a framework-agnostic bridge |
| Code Mode | Experimental @ai-sdk/code-mode (QuickJS, Node 22+) | 6 isolate drivers: Node, QuickJS WASM, QuickJS Bun, Cloudflare, Daytona, E2B |
| Coding agents | Experimental HarnessAgent, centered on Vercel Sandbox | Claude Code, Codex, OpenCode, Grok Build, and any ACP agent, on local process, Docker, Daytona, E2B, Vercel, Cloudflare, Sprites, and more |
| Durable agent runs | Workflow SDK on Vercel | Detach, journal, takeover, and reaper on any sandbox provider |
| Computer use | Provider-hosted computer-use tools | Provider-agnostic, with local, Playwright, and sandbox drivers |
| Image | generateImage() (stable) | Stable, typed per model |
| Video | experimental_generateVideo() | Stable, with the async job lifecycle streamed to the client |
| Speech and transcription | Marked experimental | Stable. Word timestamps and diarization |
| Music and sound effects | - | generateAudio() |
| Live worlds and live video | - | Reactor and World Labs adapters |
| Realtime voice | Experimental experimental_useRealtime | Stable: OpenAI, Grok, ElevenLabs |
| Devtools | devToolsMiddleware and a local inspector (server, dev only) | In-app panel for the server and the client |
To move an app, start with Migration from Vercel AI SDK.