Prerequisites
- Node.js 18.18 or later
- Next.js 14 with the App Router (stable, not canary)
- npm or pnpm
- An API key from OpenAI, or any OpenAI-compatible endpoint. If you want a single base URL that fronts 240+ models with automatic fallback when a provider is degraded, n4n.ai works as a drop-in replacement.
- Working knowledge of TypeScript, React, and HTTP
Why the Vercel AI SDK
A vercel ai sdk nextjs full-stack chatbot keeps frontend and backend in one codebase while the SDK handles Server-Sent Events, message normalization, and React state. You write a route that streams tokens; the useChat hook gives you messages, input, and handleSubmit without custom WebSocket code or manual fetch parsing. The alternative—hand-rolling streaming parsers—is error-prone and steals time from product work.
1. Scaffold and install
npx create-next-app@latest chatbot --ts --app --tailwind --src-dir --import-alias "@/*"
cd chatbot
npm install ai @ai-sdk/openai zod
This produces src/app, Tailwind CSS, and the AI packages. @ai-sdk/openai implements the OpenAI chat protocol but accepts a custom baseURL, so the same code runs against OpenAI, Azure, or a gateway.
Expected project layout after install:
src/
app/
api/chat/route.ts (to create)
page.tsx
layout.tsx
components/
chat.tsx (to create)
2. Configure the provider
Create .env.local:
# Direct OpenAI
OPENAI_API_KEY=sk-...
# Or an OpenAI-compatible gateway:
# OPENAI_BASE_URL=https://api.n4n.ai/v1
# OPENAI_API_KEY=your-gateway-key
When OPENAI_BASE_URL is set, the provider routes all traffic there. The vercel ai sdk nextjs full-stack chatbot logic stays identical. No conditional imports, no feature flags.
3. Build the streaming API route
Create src/app/api/chat/route.ts:
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
export const runtime = 'edge';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o-mini'),
messages,
});
return result.toDataStreamResponse();
}
streamText accepts the standard chat messages array ({role, content}) and returns a result object. toDataStreamResponse() serializes it as a streaming HTTP response that the client hook consumes. The edge runtime keeps cold starts low for simple proxies.
Checkpoint—curl the route before writing UI:
curl -X POST http://localhost:3000/api/chat \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Say hello in one word"}]}'
Expected raw output (truncated):
data: {"type":"text-start"}
data: {"type":"text","value":"Hi"}
data: {"type":"text-end"}
data: {"type":"finish","finishReason":"stop"}
The exact token varies, but the data: framing confirms streaming works.
4. Client chat component
Create src/components/chat.tsx:
'use client';
import { useChat } from 'ai/react';
export function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } =
useChat();
return (
<div className="mx-auto max-w-lg p-4">
<div className="space-y-2">
{messages.map((m) => (
<div key={m.id} className="whitespace-pre-wrap">
<span className="font-bold">{m.role}: </span>
{m.content}
</div>
))}
</div>
<form onSubmit={handleSubmit} className="mt-4 flex gap-2">
<input
className="flex-1 rounded border p-2"
value={input}
onChange={handleInputChange}
placeholder="Type a message"
disabled={isLoading}
/>
<button
type="submit"
className="rounded bg-black px-4 py-2 text-white"
disabled={isLoading}
>
Send
</button>
</form>
</div>
);
}
useChat POSTs to /api/chat by default and appends streamed tokens to messages. This hook is the backbone of a vercel ai sdk nextjs full-stack chatbot: it manages input state, submission, and reconciliation of partial responses.
5. Mount the page
Edit src/app/page.tsx:
import { Chat } from '@/components/chat';
export default function Page() {
return (
<main className="min-h-screen py-10">
<h1 className="mb-6 text-center text-2xl font-semibold">Chatbot</h1>
<Chat />
</main>
);
}
Run npm run dev. Open http://localhost:3000, type “What is 2+2?”, and the assistant message renders token by token. The network tab shows a single POST with a 200 status and a chunked body.
6. Add a tool call
Production chatbots invoke actions. Extend the route with a weather tool:
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o-mini'),
messages,
tools: {
getWeather: tool({
parameters: z.object({ city: z.string() }),
execute: async ({ city }) => {
// Stub; replace with real API
return { city, tempC: 21 };
},
}),
},
});
return result.toDataStreamResponse();
}
The SDK streams a tool invocation, runs execute server-side, and feeds the result back to the model. On the client, message.toolInvocations exposes the call and result if you want to render a “Calling weather…” badge.
Sample UI exchange after adding the tool:
user: What's the weather in Berlin?
assistant: The temperature in Berlin is 21°C.
7. Inspect the stream protocol
The data stream is not raw text. Each line is a JSON object with a type. Relevant types:
{"type":"text-start"}
{"type":"text","value":"The"}
{"type":"text","value":" temperature"}
{"type":"tool-invocation","toolInvocation":{"toolName":"getWeather","args":{"city":"Berlin"}}}
{"type":"tool-result","toolInvocation":{"toolName":"getWeather","result":{"tempC":21}}}
{"type":"finish","finishReason":"stop","usage":{"promptTokens":38,"completionTokens":9}}
Understanding this helps when debugging proxy layers or writing custom clients.
8. Production notes
- Switch
runtimeto'nodejs'if you need filesystem or specific Node APIs; edge is fine for pure streaming proxies. - Lock the route with auth by reading a session cookie in
POSTand returning401before callingstreamText. - If you route through n4n.ai, you can send provider cache-control hints via request headers; the gateway forwards them and meters per-token usage without extra code.
- The
useChathook acceptsapi: '/custom/path'andonErrorfor centralized error reporting.
That is a complete vercel ai sdk nextjs full-stack chatbot: one route, one hook, one component. Add persistence with a database or RAG with embeddings once the prototype proves its worth—not before.