How to Build an AI Chatbot Using Next.js and an API
Most chatbot tutorials stop at a text box that echoes a model's reply. Production chatbots need more: streamed responses so users are not staring at a spinner, a server boundary that keeps API keys secret, a system prompt that keeps the bot on topic, rate limiting, and a way to store conversations. An AI chatbot built with Next.js is a web interface whose React front end sends user messages to a Next.js Route Handler, which calls a large language model API and streams the generated reply back to the browser.
Quick Answer: Create a Next.js App Router project, add a Route Handler at app/api/chat/route.ts that calls a model through an AI SDK or provider API and streams the reply, build a client component that sends messages and renders the stream, keep keys on the server, and add a system prompt, input limits, and rate limiting.
How Does a Next.js Chatbot Work Under the Hood?
A Next.js chatbot works by splitting responsibilities across the client and the server. The browser holds the conversation state and renders messages. The server receives the message history, adds a system prompt, calls the model provider's API with a secret key, and streams tokens back as they are generated. The user sees text appear progressively, which feels far faster than waiting for a complete reply.
This architecture is one of the clearest arguments for using a framework rather than a client-only app. If you are still weighing that decision, our breakdown of Next.js versus plain React explains why having server code in the same project matters for features like this. In a client-only React app, you would need a separate backend just to hide the API key, because any key shipped to the browser can be extracted by anyone.
A few terms will come up repeatedly. A system prompt is the hidden instruction that defines the bot's role, tone, and boundaries. Streaming sends the response in chunks as the model generates it. Context window is the maximum amount of text the model can consider at once, which limits how much history you can send. Tools, sometimes called function calling, let the model request that your code run a function, such as looking up an order, and then use the result in its answer.
You can call a provider's REST API directly, but most Next.js teams use the Vercel AI SDK, which provides a unified interface across model providers, streaming helpers for Route Handlers, and React hooks such as useChat that manage message state and stream parsing on the client.
Step-by-Step: Building the Chatbot
The build has five core steps, and each one is small. The examples below use the AI SDK's current patterns; check the documentation for the exact version you install, because helper names evolve between major releases.
- Create the project. Run
npx create-next-app@latestand choose TypeScript and the App Router. Install the AI SDK packages withnpm install ai @ai-sdk/react. - Configure credentials. Store your model provider or gateway key in
.env.local. Never prefix it withNEXT_PUBLIC_, because that exposes it to the browser. - Write the Route Handler. Create
app/api/chat/route.ts, read the messages from the request, call the model with a system prompt, and return the stream. - Build the chat UI. Create a client component that uses the
useChathook to send messages and render the streamed replies. - Harden it. Add input length limits, rate limiting, error states, and logging before anyone outside your team uses it.
A minimal Route Handler looks like this:
import { streamText, convertToModelMessages } from 'ai';
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: 'provider/model-name',
system: 'You are a support assistant for Acme. Answer only questions about Acme products.',
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}And a minimal client component:
'use client';
import { useChat } from '@ai-sdk/react';
import { useState } from 'react';
export function Chat() {
const { messages, sendMessage, status } = useChat();
const [input, setInput] = useState('');
return (
<form onSubmit={(e) => { e.preventDefault(); sendMessage({ text: input }); setInput(''); }}>
{messages.map((m) => (
<div key={m.id}>{m.parts.map((p, i) => p.type === 'text' ? <p key={i}>{p.text}</p> : null)}</div>
))}
<input value={input} onChange={(e) => setInput(e.target.value)} disabled={status !== 'ready'} />
</form>
);
}Replace the model string with a current model ID from your provider or gateway's model list. That is genuinely all it takes to get a streaming chatbot running locally.
Choosing How to Call the Model API
You have three realistic ways to connect a Next.js chatbot to a model, and each fits a different situation.
| Approach | How It Works | Best For | Trade-off |
|---|---|---|---|
| Direct provider SDK or REST API | Call one provider's endpoint with its own key and format | Teams committed to a single provider's specific features | Switching providers means rewriting request and stream handling |
| AI SDK with a provider package | Unified functions such as streamText wrap the provider | Most Next.js apps wanting clean streaming and React hooks | Adds a dependency you must keep updated |
| AI SDK through an AI gateway | One endpoint routes to many providers by model ID | Apps that want to swap or compare models easily | Another service in the request path to monitor |
| Self-hosted open model | Your own inference server exposes a compatible API | Strict data residency or very high, predictable volume | You own GPUs, scaling, and model updates |
For a first production chatbot, the AI SDK with either a provider package or a gateway is the most efficient path, because streaming, message formats, and tool calls are already solved.
What Makes a Chatbot Useful Rather Than a Demo
A chatbot becomes useful when it is grounded in your actual information and constrained to tasks it can do well. What follows is practitioner analysis from shipping support and internal assistants, not vendor benchmarks.
Grounding comes first. A general model does not know your pricing, policies, or product details, and it will confidently guess if asked. The fix is retrieval: store your documentation as embeddings, search for the most relevant passages for each question, and include them in the prompt. Alternatively, expose tools that query your database or API so the model fetches live facts instead of inventing them.
Scope comes second. A focused system prompt that states what the bot handles, what it must refuse, and when to hand off to a human reduces off-topic and risky answers. Test it with adversarial prompts, such as attempts to make it ignore instructions or reveal the system prompt.
Cost and latency come third. Every message resends the conversation history, so long chats grow more expensive and slower. Trim older messages or summarize them once the conversation passes a sensible length. Choose a smaller, faster model for routine questions and reserve larger models for complex reasoning.
Developer speed matters too. AI coding assistants are especially good at generating boilerplate like Route Handlers, Zod schemas, and UI states. Our roundup of free AI coding assistants for JavaScript covers tools that can scaffold much of this chatbot code, as long as you review what they produce.
Common Mistakes and How to Avoid Them
The most damaging mistake is exposing the API key to the browser. Anything prefixed with NEXT_PUBLIC_ or imported into a client component is visible to users. Keep every model call inside a Route Handler or Server Action.
- No rate limiting. A public endpoint that calls a paid model can be abused within hours. Limit requests per IP or per user with a store such as Redis.
- Unbounded input. Reject messages above a sensible character limit and cap the number of history messages accepted from the client.
- Trusting client-sent history blindly. A user can edit the messages array, including fake assistant turns. For sensitive bots, store the canonical history on the server.
- No error state. Providers occasionally time out or rate-limit you. Show a clear retry message instead of a frozen interface.
- Ignoring serverless timeouts. Long generations can exceed default function durations. Set an appropriate maximum duration for the route.
- Forgetting CJK input handling. If Enter submits the message, check that the user is not mid-composition in an input method editor, or Chinese, Japanese, and Korean users will send half-typed messages.
Production Checklist: A Support Bot for a SaaS Product
Here is the checklist I use before a support chatbot goes live, illustrated with a bot that answers questions about a project management SaaS.
Knowledge. Help center articles are chunked by heading, embedded, and stored in a vector-capable database. Each question retrieves the top relevant chunks, which are passed to the model with an instruction to answer only from that material and to say when it does not know.
Tools. A getAccountPlan tool reads the signed-in user's plan from the database so the bot can answer billing questions accurately. The tool checks the session server-side and never accepts a user ID from the model's arguments.
Boundaries. The system prompt forbids legal and refund promises and instructs the bot to offer a human handoff link for account cancellations or complaints.
Safety and abuse. Input is capped, rate limits apply per user, and only authenticated users can access the chat route.
Persistence. Each conversation is saved with a user ID, timestamps, and message parts so support staff can review transcripts and the team can find questions the bot answers poorly.
Monitoring. Logs capture latency, token usage, errors, and thumbs-up or thumbs-down feedback. Every week, the team reviews negatively rated answers and fixes the underlying documentation or prompt.
Launch. The bot is released to a small group of customers first, then expanded once the feedback rate stabilizes. This staged rollout catches embarrassing answers before they reach everyone.
Key Takeaways
- A Next.js chatbot keeps the model call in a server Route Handler so API keys never reach the browser.
- Streaming responses dramatically improve perceived speed and are built into the AI SDK's server and client helpers.
- Grounding answers in your documentation or live data through retrieval and tools prevents confident guessing.
- Rate limiting, input caps, and authentication are required before exposing a paid model endpoint publicly.
- Storing conversations and reviewing poorly rated answers is the fastest way to improve a chatbot after launch.
Frequently Asked Questions
Can I build an AI chatbot with Next.js for free?
You can build and run the code for free, since Next.js and the AI SDK are open source. Model usage is usually billed by tokens, though some providers and gateways offer free tiers or credits. You can also run an open-weight model locally during development to avoid API costs entirely.
Which API is best for a Next.js chatbot?
The best API depends on your needs for quality, speed, cost, and data handling. Using the AI SDK lets you switch between providers by changing a model ID, so you can test several options with real user questions. Start with a fast, inexpensive model and upgrade only where answers fall short.
How do I keep my API key secure in Next.js?
Store the key in a server-only environment variable without the NEXT_PUBLIC prefix, and use it only inside Route Handlers, Server Actions, or other server code. Never import it into client components. On your hosting platform, add it as an encrypted environment variable rather than committing it to the repository.
How do I make my chatbot answer questions about my own content?
Use retrieval-augmented generation. Split your content into chunks, create embeddings, and store them in a database with vector search. For each question, retrieve the most relevant chunks and include them in the prompt with an instruction to answer only from that material and admit when information is missing.
How can I save chat history in a Next.js chatbot?
Save messages to a database from the server when a response finishes, keyed by a conversation ID and the authenticated user's ID. On page load, fetch the stored messages and pass them to the chat hook as initial state. MongoDB and Postgres both work well for storing message records.
Why is my Next.js chatbot response not streaming?
Streaming usually breaks when the route awaits the full response before returning, when a proxy buffers output, or when the client does not read the stream. Return the SDK's streaming response directly from the Route Handler, and check that any reverse proxy, such as Nginx, has response buffering disabled for that path.
Conclusion
The most important design decision is keeping every model call on the server, behind authentication, rate limits, and a focused system prompt; everything else can be improved after launch. Get the streaming route and UI working first, then ground the bot in your own content. When you are ready to keep conversations across sessions, your next step is to persist chat history in MongoDB using a connection pattern built for Next.js.




