streamText, generateObject, useChat, and provider switching in under 30 minutes

What the Vercel AI SDK is

The Vercel AI SDK is a TypeScript library that makes it easy to add streaming LLM features to web applications — especially Next.js apps. It abstracts over multiple LLM providers (OpenAI, Anthropic, Google, Mistral, and more), provides React hooks for building chat UIs, and handles the streaming plumbing that would otherwise require significant boilerplate.

It has two main layers: the AI SDK Core (server-side, framework-agnostic) and the AI SDK UI (client-side React hooks). This guide covers both.

Installation

npm install ai @ai-sdk/openai
# Or for Anthropic:
npm install ai @ai-sdk/anthropic
# Or Google:
npm install ai @ai-sdk/google
 

Core: streamText — streaming text from the server

streamText is the main server-side function. It streams tokens from any supported LLM and returns a response that can be piped to the client.

// app/api/chat/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
 
export async function POST(req: Request) {
  const { messages } = await req.json();
 
  const result = streamText({
    model: openai('gpt-4o-mini'),
    system: 'You are a helpful assistant.',
    messages,                          // OpenAI message format
    maxTokens: 1024,
    temperature: 0.7,
  });
 
  return result.toDataStreamResponse();  // streams to client
}
 

UI: useChat — the React hook

useChat connects your React component to the streaming API route above. It manages messages, loading state, and input handling.

// app/page.tsx
'use client';
import { useChat } from 'ai/react';
 
export default function ChatPage() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: '/api/chat',
  });
 
  return (
    <div>
      <div>
        {messages.map(m => (
          <div key={m.id}>
            <strong>{m.role}:</strong> {m.content}
          </div>
        ))}
        {isLoading && <div>Thinking...</div>}
      </div>
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} placeholder='Ask anything...' />
        <button type='submit' disabled={isLoading}>Send</button>
      </form>
    </div>
  );
}
 
useChat automatically appends each exchange to the messages array and re-renders as tokens arrive. You do not need to manage loading state or streaming manually.

generateObject — structured data from LLMs

generateObject returns a typed, validated Zod object instead of text. Use it whenever you need structured output from an LLM.

// app/api/extract/route.ts
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
 
const ProductSchema = z.object({
  name: z.string(),
  price: z.number(),
  category: z.enum(['electronics', 'clothing', 'food', 'other']),
  inStock: z.boolean(),
  tags: z.array(z.string()),
});
 
export async function POST(req: Request) {
  const { description } = await req.json();
 
  const { object } = await generateObject({
    model: openai('gpt-4o'),
    schema: ProductSchema,
    prompt: `Extract product details from: ${description}`,
  });
 
  return Response.json(object);  // fully typed and validated
}
 

useObject — streaming structured data to the client

useObject is the client-side counterpart to generateObject with streamObject on the server. It delivers partial objects as they stream, updating the UI progressively.

// Server: app/api/review/route.ts
import { streamObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
 
const ReviewSchema = z.object({
  rating: z.number().min(1).max(5),
  summary: z.string(),
  pros: z.array(z.string()),
  cons: z.array(z.string()),
});
 
export async function POST(req: Request) {
  const { text } = await req.json();
  const result = streamObject({
    model: openai('gpt-4o'),
    schema: ReviewSchema,
    prompt: `Analyse this product review: ${text}`,
  });
  return result.toTextStreamResponse();
}
 
// Client: components/Review.tsx
// 'use client';
// import { useObject } from 'ai/react';
// const { object, submit, isLoading } = useObject({ api: '/api/review', schema: ReviewSchema });
// object.rating arrives as soon as that field is generated
 

Provider switching

The SDK uses a unified interface across providers. Switching is a one-line change.

import { openai }    from '@ai-sdk/openai';
import { anthropic } from '@ai-sdk/anthropic';
import { google }    from '@ai-sdk/google';
 
// Same API, different providers
const gpt4o   = openai('gpt-4o');
const claude  = anthropic('claude-opus-4-6-20251101');
const gemini  = google('gemini-2.0-flash');
 
// Swap the model variable — everything else stays the same
const result = streamText({
  model: process.env.USE_CLAUDE ? claude : gpt4o,
  messages,
});
 

Edge Runtime compatibility

All AI SDK server functions run on the Edge Runtime in Next.js. Add the export to any route:

export const runtime = 'edge';  // add to any route.ts
 
// Edge Runtime limitations to be aware of:
// - No Node.js built-ins (fs, path, etc.)
// - 25MB response limit on Vercel free tier
// - Cold starts are faster but warm memory is limited
 

generateText — non-streaming server calls

Use generateText when you do not need streaming — batch processing, background jobs, or when the response feeds into another step.

import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
 
const { text, usage } = await generateText({
  model: anthropic('claude-haiku-4-5-20251001'),  // cheapest model for batch work
  prompt: 'Classify this support ticket as: billing, technical, or general: ' + ticket,
  maxTokens: 10,
});
 
console.log(text);         // 'billing'
console.log(usage.totalTokens);  // track costs
 

Quick reference

Function / Hook Use case Streaming
streamText Server: stream text to client Yes
generateText Server: get text (no stream needed) No
streamObject Server: stream structured JSON Yes
generateObject Server: get validated object No
useChat Client: chat UI with history Yes
useCompletion Client: single-turn completion Yes
useObject Client: streaming structured data Yes