Enterprise DNA

Omni by Enterprise DNA

Enterprise DNA Resources

Step-by-step how-tos. Practical AI operating-system thinking for owners, operators, and teams doing real work.

220k+

Data professionals

Omni

AI agents and apps

Audit

Map the manual work

Guide Intermediate General

AI SDK Next.js Tutorial: Build a Chat App Step by Step

A practical walkthrough for building a streaming chat app with Vercel AI SDK and Next.js, from setup to deployment.

Sam McKay |
AI SDK Next.js Tutorial: Build a Chat App Step by Step

What AI SDK and Next.js Actually Are

The Vercel AI SDK is a TypeScript library that handles the repetitive parts of building AI features in a web app. It wraps provider APIs (OpenAI, Anthropic, Google, Mistral, and a long tail of others) behind a single interface and gives you React hooks that manage streaming, loading states, and message history without you writing the plumbing.

Next.js is the React framework it pairs with most naturally. App Router, server actions, edge runtime, and route handlers all work cleanly with the SDK. You can technically use the AI SDK with other frameworks, but the documentation, examples, and ecosystem are tuned for Next.js.

What it does under the hood is fairly specific. It standardises three things that would otherwise be different for every provider. First, the request shape, so you pass a model and messages the same way regardless of whether the underlying call hits GPT, Claude, or Gemini. Second, the streaming protocol, using server-sent events and a typed message format that React hooks can consume in real time. Third, the response handling for things like tool calls and structured outputs, which are areas where each provider’s API diverges in annoying ways.

What it does not do is act as an agent framework. It does not give you a planner, a memory layer, a vector store, or a routing system. It is the transport and UI layer. If you need those things, you bring them in yourself or combine it with something like LangGraph or Mastra.

Setup and Authentication

The fastest path to a working setup is to scaffold a Next.js project and add the AI SDK on top.

Start by creating a new Next.js project with the App Router. The standard command is npx create-next-app@latest my-chat-app and answer the prompts with TypeScript yes, ESLint yes, Tailwind optional, App Router yes, and no to the src directory unless you have a preference. This produces a fresh project you can run with npm run dev.

Next, install the AI SDK core package and at least one provider package. The core is ai and the providers are scoped, so for OpenAI you run npm install ai @ai-sdk/openai and for Anthropic you run npm install @ai-sdk/anthropic. You can install several providers in the same project. The SDK will pick up whichever ones you import.

Now set up authentication. The AI SDK itself does not authenticate to providers. It expects you to pass an API key. In development, the cleanest approach is a local .env.local file. Add lines like OPENAI_API_KEY=sk-... and ANTHROPIC_API_KEY=sk-ant-... depending on which providers you wired in. Never commit this file. Add .env.local to .gitignore if it is not already there.

In Vercel or any other deployment platform, the equivalent is to set the same variables as project secrets. The SDK reads process.env.OPENAI_API_KEY automatically when you instantiate a provider without passing a key explicitly. This is the pattern you want to use, because it keeps secrets out of your code.

For end-user authentication of the chat app itself, that is a separate concern. The SDK does not handle it. Most production setups put the chat route behind NextAuth, Clerk, or a custom session check. For this guide, assume the route is open or guarded by your own middleware.

First Working Example

The minimum working chat app has two files. A route handler that streams responses from the model, and a client component that renders the conversation.

Create the route at app/api/chat/route.ts. The shape looks like this in spirit, using the OpenAI provider as the example. The handler reads the messages from the request body, picks a model, and returns a streaming response using the SDK’s streamText helper. The useChat hook on the client knows how to consume that stream because both ends speak the same protocol.

On the client, create a component called Chat.tsx that imports useChat from ai/react. Call the hook to get messages, input, handleInputChange, and handleSubmit. Render messages as a list and bind the form to handleSubmit. The hook handles streaming tokens into the UI as they arrive, including partial messages while the model is still generating.

When you run npm run dev and open the page, you can send a message and watch the response stream in token by token. That is the basic loop. It takes about ten minutes to get here once your environment is ready.

Two things to double-check if it does not work. First, the API key must be available at runtime, which means a server restart after you add it to .env.local. Second, the route must be a Node.js runtime or an Edge runtime that the SDK supports, which the current version of the SDK does. If you are on an older Next.js version, double-check the edge compatibility of the specific model wrapper.

Key Settings That Matter

Most people ignore the dials on the model call and then wonder why results are inconsistent. A few settings have outsized impact.

Temperature is the first one. The default in most providers is around 1, which is fine for chat but produces wandering responses for structured tasks. For extraction, classification, or tool selection, drop it to 0 or close to it. For creative writing, raise it. This is the single most influential parameter for output character.

Max tokens caps the response length. The default is often the provider’s maximum, which can produce very long and expensive replies. For a chat UI, set a sensible cap like 500 to 1000 for conversational turns. For summarisation, allow more.

System prompt placement matters. The SDK takes a system field on streamText. Putting the instructions there keeps them outside the visible message history, which is useful when you store and replay messages later. If you cram the instructions into the first user message, you will get confused later when you cannot tell user content from system content.

Tool calling configuration is where the SDK earns its keep. You define tools as Zod schemas with tool from the AI SDK. The model returns a structured call, you execute it in your execute function, and the SDK feeds the result back into the conversation. The thing to get right is the schema. Vague Zod schemas produce vague tool use. Tight schemas with descriptions on every field are the difference between a tool that works and one that hallucinates parameters.

Streaming strategy is another lever. streamText is the default and what you want for chat. generateText returns a complete response and is better for backend jobs, batch processing, or anywhere you do not need a live UI. The two share most options, so pick based on whether the user is waiting.

Finally, message persistence. The default useChat keeps messages in client state. If you refresh, the conversation is gone. For a real product you need to wire onFinish or use the experimental useChat options to send messages to a database, then reload them on mount. This is the part most tutorials skip and most teams underestimate.

Where It Shines

The AI SDK genuinely excels at a few specific jobs. Building a streaming chat UI is the obvious one. The combination of streamText on the server and useChat on the client removes about two days of boilerplate per feature. Token-by-token rendering, abort buttons, retry logic, and loading indicators all come for free.

Multi-provider support is the second strong suit. If your business needs to switch between GPT