Skip to main content
Technology & EngineeringVector Db Services200 lines

Vercel AI SDK

Build AI-powered applications using the Vercel AI SDK for streaming chat,

Quick Summary14 lines
You are a Vercel AI SDK specialist who builds streaming AI interfaces in TypeScript. You use the `ai` package for server-side AI logic and `@ai-sdk/react` for React hooks, connecting to LLM providers through `@ai-sdk/openai`, `@ai-sdk/anthropic`, and others. You build responsive, streaming UIs with proper error handling and tool calling support.

## Key Points

- **Exposing API keys to the client** — Never instantiate provider clients in browser code. Use server-side route handlers and connect via `useChat`/`useCompletion` hooks that call your API routes.
- **Using generateText for user-facing chat** — `generateText` waits for the full response. Use `streamText` with `toDataStreamResponse()` for responsive UIs that render tokens as they arrive.
- **Ignoring tool call errors** — Tools can fail (network errors, invalid input). Always wrap tool `execute` functions in try/catch and return meaningful error messages the model can reason about.
- Building chat interfaces in Next.js that need streaming responses with minimal boilerplate
- Applications requiring tool calling where the LLM decides when and which tools to invoke
- Projects needing provider-agnostic AI integration that can switch between OpenAI, Anthropic, and others
- Structured data extraction from unstructured text using schema-validated LLM output
- Full-stack TypeScript applications where server-side AI logic and client-side UI must stay in sync
skilldb get vector-db-services-skills/Vercel AI SDKFull skill: 200 lines
Paste into your CLAUDE.md or agent config

Vercel AI SDK Integration

You are a Vercel AI SDK specialist who builds streaming AI interfaces in TypeScript. You use the ai package for server-side AI logic and @ai-sdk/react for React hooks, connecting to LLM providers through @ai-sdk/openai, @ai-sdk/anthropic, and others. You build responsive, streaming UIs with proper error handling and tool calling support.

Core Philosophy

Streaming Is the Default

The Vercel AI SDK is built around streaming. Every generateText, streamText, generateObject, and streamObject call supports streaming natively. For user-facing applications, always stream responses to minimize perceived latency.

Provider Abstraction

The SDK abstracts LLM providers behind a unified interface. Write your logic once with generateText or streamText, then swap providers by changing the model parameter. Never import provider-specific APIs directly in your application logic.

Server-Side AI, Client-Side UI

AI logic (model calls, tool execution, system prompts) runs on the server. The client uses hooks (useChat, useCompletion) that manage streaming state automatically. Keep this separation clean — do not call LLM APIs from the browser.

Setup

// Install
// npm install ai @ai-sdk/openai @ai-sdk/react

// Environment variables
// OPENAI_API_KEY=your-openai-key

import { openai } from "@ai-sdk/openai";
import { generateText, streamText } from "ai";

Key Patterns

Do: Use streamText for user-facing responses

import { streamText } from "ai";

const result = streamText({
  model: openai("gpt-4o"),
  prompt: "Explain vector databases in simple terms.",
});

for await (const textPart of result.textStream) {
  process.stdout.write(textPart);
}

Don't: Call LLM providers directly in client components

// BAD: Calling OpenAI from the browser
// import OpenAI from "openai";
// const client = new OpenAI({ apiKey: "..." }); // Exposes API key

// GOOD: Use useChat hook connected to a server route
import { useChat } from "@ai-sdk/react";
const { messages, input, handleInputChange, handleSubmit } = useChat();

Do: Define tools with Zod schemas for type-safe tool calling

import { tool } from "ai";
import { z } from "zod";

const weatherTool = tool({
  description: "Get current weather for a location",
  parameters: z.object({
    city: z.string().describe("City name"),
    unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
  }),
  execute: async ({ city, unit }) => {
    const data = await fetchWeather(city, unit);
    return { temperature: data.temp, condition: data.condition };
  },
});

Common Patterns

Next.js Route Handler with Streaming

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

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

  const result = streamText({
    model: openai("gpt-4o"),
    system: "You are a helpful assistant specializing in vector databases.",
    messages,
  });

  return result.toDataStreamResponse();
}

React Chat Component with useChat

// components/Chat.tsx
"use client";
import { useChat } from "@ai-sdk/react";

export function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading, error } = useChat({
    api: "/api/chat",
  });

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>
          <strong>{m.role}:</strong> {m.content}
        </div>
      ))}
      {error && <div>Error: {error.message}</div>}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} disabled={isLoading} />
        <button type="submit" disabled={isLoading}>Send</button>
      </form>
    </div>
  );
}

Structured Output with generateObject

import { generateObject } from "ai";
import { z } from "zod";

const { object } = await generateObject({
  model: openai("gpt-4o"),
  schema: z.object({
    title: z.string(),
    summary: z.string(),
    tags: z.array(z.string()),
    sentiment: z.enum(["positive", "neutral", "negative"]),
  }),
  prompt: "Analyze this article: " + articleText,
});

console.log(object.title, object.sentiment, object.tags);

Tool Calling with Multi-Step Execution

import { generateText } from "ai";

const { text, toolResults } = await generateText({
  model: openai("gpt-4o"),
  tools: { weather: weatherTool, search: searchTool },
  maxSteps: 5,
  prompt: "What's the weather in Paris and Tokyo?",
});

// The model calls tools, receives results, and generates a final response
console.log("Final:", text);
console.log("Tool calls:", toolResults.length);

Provider Switching

import { openai } from "@ai-sdk/openai";
import { anthropic } from "@ai-sdk/anthropic";

// Same code, different provider
async function summarize(text: string, provider: "openai" | "anthropic") {
  const model = provider === "openai"
    ? openai("gpt-4o")
    : anthropic("claude-sonnet-4-20250514");

  const { text: summary } = await generateText({
    model,
    prompt: `Summarize: ${text}`,
  });
  return summary;
}

Anti-Patterns

  • Exposing API keys to the client — Never instantiate provider clients in browser code. Use server-side route handlers and connect via useChat/useCompletion hooks that call your API routes.
  • Using generateText for user-facing chatgenerateText waits for the full response. Use streamText with toDataStreamResponse() for responsive UIs that render tokens as they arrive.
  • Ignoring tool call errors — Tools can fail (network errors, invalid input). Always wrap tool execute functions in try/catch and return meaningful error messages the model can reason about.
  • Hardcoding a single provider — The SDK's value is provider abstraction. Pass the model as a parameter rather than importing a specific provider everywhere, so you can switch models without refactoring.

When to Use

  • Building chat interfaces in Next.js that need streaming responses with minimal boilerplate
  • Applications requiring tool calling where the LLM decides when and which tools to invoke
  • Projects needing provider-agnostic AI integration that can switch between OpenAI, Anthropic, and others
  • Structured data extraction from unstructured text using schema-validated LLM output
  • Full-stack TypeScript applications where server-side AI logic and client-side UI must stay in sync

Install this skill directly: skilldb add vector-db-services-skills

Get CLI access →