Build a Hello World AI Chat App with Next.js and an LLM API

Build a Hello World AI Chat App with Next.js and an LLM API

September 10, 2026

A “Hello World” AI chat app is the smallest useful version of a conversational application: a text input, a message history, a backend call to an LLM, and a response that appears in the UI. It is intentionally simple, but it covers the exact building blocks you need for production chat experiences: state management, secure API calls, streaming responses, and robust server-side handling.

Next.js is a strong fit for this kind of app because its App Router is designed around React Server Components, Server Functions, and Route Handlers, with built-in streaming and navigation optimizations. That combination lets you keep the UI responsive while moving sensitive logic, such as API key usage and prompt assembly, to the server. It also makes it easier to scale from a demo to a real product without rewriting the core architecture. (nextjs.org)

In this post, you’ll build a practical AI chat app architecture in Next.js 15+, learn how to choose an LLM API, and see how to implement streaming, memory, error handling, and production-ready concerns. The examples are framed for developers who want a clean foundation they can extend into a real SaaS assistant, support bot, internal knowledge tool, or onboarding helper.

Chat app architecture overview

1) Introduction: What a Hello World AI Chat App Is and Why Next.js Fits

A Hello World AI chat app is not about sophisticated prompt engineering or advanced retrieval. It is the minimal loop of user intent and model output: the user types a message, the server forwards context to an LLM API, and the assistant returns a response. That simple loop is the ideal starting point because it exposes the most important architectural decisions early: where state lives, how requests are authenticated, how messages stream, and how errors are handled.

Next.js fits this use case especially well because the framework gives you a split between server and client responsibilities by default. Pages and layouts are Server Components, which means you can render data on the server and stream the result to the browser, while still using Client Components for interactive parts like text inputs, buttons, and optimistic UI updates. This division is useful in chat apps because the conversation chrome can be server-rendered, while the composer and live message updates stay interactive on the client. (nextjs.org)

Another advantage is that Next.js Route Handlers use the standard Web Request and Response APIs inside the app directory. That makes it natural to create a POST endpoint for message submission without introducing a separate backend framework. For many teams, that means one codebase, one deployment pipeline, and fewer integration boundaries to manage. Route Handlers also support streaming-friendly patterns, which matter because chat UX improves dramatically when users see the assistant typing instead of waiting for a full completion. (nextjs.org)

In practice, the “Hello World” version should still follow production-shaped boundaries: the browser sends only user messages, the server constructs prompts and handles the API key, and the UI renders tokens or partial text as they arrive. That structure is small enough to learn quickly, but realistic enough to evolve into a full AI product without a rewrite.

2) Why Next.js for AI Chat Apps: App Router, Server Components, Route Handlers, and Built-in Streaming

The App Router is one of the main reasons Next.js is compelling for AI chat apps. It is built on React’s latest primitives, including Server Components, Suspense, and Server Functions, which makes it well suited for apps that need both fast initial rendering and interactive client-side behavior. In a chat application, that means you can render the shell of the page immediately, fetch or hydrate conversation data on the server, and then let the message composer behave like a normal client interface. (nextjs.org)

Route Handlers are equally important. Instead of building a separate Express or Fastify backend for a simple LLM proxy, you can define app/api/chat/route.ts and expose a POST endpoint using the standard Web Request/Response model. Next.js documents Route Handlers as the App Router equivalent of API Routes, available inside the app directory and suitable for custom request handling. For a chat app, that gives you a clean place to validate input, attach metadata, call the model provider, and stream the response back to the browser. (nextjs.org)

Built-in streaming is the other major reason to choose Next.js. Next.js supports streaming route segments and component-level streaming through loading.js or <Suspense>, which helps the page become useful before all data is ready. For AI chat, streaming is even more valuable because the model output can be incrementally displayed as tokens arrive. The OpenAI Responses API also supports server-sent streaming events when stream is enabled, so the model’s output can reach the user progressively rather than all at once. (nextjs.org)

From an engineering perspective, this reduces perceived latency, makes failure modes easier to tolerate, and keeps your application architecture compact. You get server-side execution for sensitive work, client-side interactivity for the chat composer, and a first-class streaming path for long or slow completions.

3) Choosing an LLM API: Responses, Chat-Style Outputs, Latency, Cost, and Developer Experience

Choosing the right LLM API is not just about raw model quality. For a chat app, you want an API that makes it easy to send structured conversation history, return text in a predictable format, stream output, and balance latency against cost. The OpenAI Responses API is a strong example because it is positioned as a general interface for generating outputs, supports streaming events, and offers models with different capability and price points. The quickstart recommends using server-sent streaming events to show results as they are generated, which maps well to chat UIs. (platform.openai.com)

A practical evaluation framework is to compare APIs on five axes:

  1. Message format: Does the API accept chat-style input easily, or do you need to manually adapt prompts?

  2. Streaming support: Can the response be consumed incrementally?

  3. Latency: How quickly does the first token arrive, and how stable is performance under load?

  4. Cost: Are token prices predictable enough for your expected traffic?

  5. Developer experience: How ergonomic is the SDK, error handling, and documentation?

For a Hello World app, developer experience often matters more than advanced features. If the API is easy to install, easy to stream, and easy to reason about, you can ship faster and keep the example code small. The OpenAI developer quickstart highlights a straightforward JavaScript SDK flow and notes that the API can output generated text directly, which reduces glue code for simple chat use cases. (platform.openai.com)

Latency and cost should be considered together. A cheaper model might still be the right choice if your app is high-volume and only needs basic assistant behavior. A more capable model may be worth the higher token cost if you need stronger reasoning, better instruction following, or fewer retries. For production chat apps, you should also think about how quickly your UI can surface partial output. Even a medium-latency model feels faster when users see tokens streaming immediately.

Finally, choose an API with a future-proof abstraction. If the provider supports structured outputs, streaming, tool use, and multi-turn state handling, your “Hello World” architecture can grow into a support bot, agent, or retrieval-augmented assistant without changing the entire data flow.

4) Project Setup: Next.js 15+, Environment Variables, SDK Installation, and App Structure

A clean project setup prevents a lot of pain later. Start with a modern Next.js app using App Router and TypeScript. Next.js 15 documentation centers on the App Router, and its project structure is designed to organize layouts, pages, and special files like route.ts and loading.tsx in a predictable way. That structure is ideal for chat apps because your conversation page, API route, and any streaming/loading states live close together. (nextjs.org)

Your basic setup should look like this:

npx create-next-app@latest hello-world-ai-chat
cd hello-world-ai-chat
npm install openai

Store your API key in an environment variable such as .env.local:

OPENAI_API_KEY=your_secret_key_here

Next.js supports server-side rendering and server execution, which means the API key should never be exposed in client-side code. Keep all provider calls inside server code, such as Route Handlers or Server Actions, and read the secret with process.env.OPENAI_API_KEY. That is the correct boundary for secure key management in a chat app.

A sensible app structure for the first version is:

app/
  page.tsx
  components/
    chat-shell.tsx
    message-list.tsx
    chat-input.tsx
  api/
    chat/
      route.ts
lib/
  llm.ts
types/
  chat.ts

This structure keeps responsibilities separated:

  • page.tsx renders the top-level route.

  • components/ contains interactive UI.

  • app/api/chat/route.ts handles server-side requests.

  • lib/llm.ts can hold shared provider logic.

  • types/chat.ts defines message shapes.

Project structure map

If you are building a production-friendly starter, also configure linting, formatting, and a clear environment variable policy. Even for a tiny demo, the discipline of separating UI, transport, and provider logic pays off immediately when you start adding persistence or streaming retries.

5) Building the UI: Chat Layout, Message List, Input Box, Send State, and Responsive Design

The UI for a Hello World AI chat app should be intentionally simple but polished. A good chat interface usually has three visible regions: a header or title bar, a scrollable message list, and a fixed input composer at the bottom. This layout works well on both desktop and mobile, and it is easy to implement with flexbox. Since Next.js uses React, you can keep the page shell as a Server Component and make the actual interactive chat composer a Client Component.

A minimal UI needs a few core states:

  • messages: the conversation history

  • input: the current draft text

  • isSending: whether a request is in flight

  • error: a displayable error message if something fails

The message list should distinguish between user and assistant messages, typically by alignment, background color, and labeling. The input box should support Enter-to-send, with Shift+Enter for newlines if you want multiline input. The send button should disable itself while a request is in progress to prevent duplicate submissions.

A basic client component pattern looks like this:

"use client";

import { useState } from "react";

type Message = {
  role: "user" | "assistant";
  content: string;
};

export function ChatShell() {
  const [messages, setMessages] = useState<Message[]>([
    { role: "assistant", content: "Hello! Ask me anything." },
  ]);
  const [input, setInput] = useState("");
  const [isSending, setIsSending] = useState(false);

  async function handleSend() {
    if (!input.trim() || isSending) return;

    const nextMessages = [...messages, { role: "user", content: input }];
    setMessages(nextMessages);
    setInput("");
    setIsSending(true);

    try {
      const res = await fetch("/api/chat", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ messages: nextMessages }),
      });

      const data = await res.json();
      setMessages([...nextMessages, { role: "assistant", content: data.reply }]);
    } finally {
      setIsSending(false);
    }
  }

  return (
    <div className="mx-auto flex min-h-screen max-w-3xl flex-col p-4">
      <div className="flex-1 space-y-4 overflow-y-auto rounded-lg border p-4">
        {messages.map((message, index) => (
          <div key={index} className={message.role === "user" ? "text-right" : "text-left"}>
            <span className="inline-block rounded-lg px-3 py-2">
              {message.content}
            </span>
          </div>
        ))}
      </div>

      <div className="mt-4 flex gap-2">
        <input
          className="flex-1 rounded border px-3 py-2"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && !e.shiftKey && handleSend()}
          placeholder="Type your message..."
        />
        <button
          className="rounded bg-black px-4 py-2 text-white disabled:opacity-50"
          onClick={handleSend}
          disabled={isSending}
        >
          {isSending ? "Sending..." : "Send"}
        </button>
      </div>
    </div>
  );
}

Responsive design matters even in a simple demo. Mobile users should see a comfortable touch target, a message list that fills the viewport, and an input that stays visible when the keyboard opens. Keeping the main container min-h-screen and using a flexible column layout helps preserve usability across screen sizes. If you want the app to feel more polished, add subtle timestamps, a typing indicator, and auto-scroll behavior so the newest assistant output is always in view.

6) Implementing the Backend: API Route or Server Action, Prompt Handling, and Secure Key Management

For most chat apps, a Route Handler is the simplest backend choice. Next.js Route Handlers are designed for custom request handling in the App Router and use standard Web APIs, which makes them a natural place to receive messages and call the LLM provider. A Server Action can also work for some workflows, but an API route is often easier to debug and more transparent for a message-based chat endpoint. (nextjs.org)

A basic handler should do four things:

  1. Validate the incoming request.

  2. Build the prompt or message array.

  3. Call the LLM API on the server.

  4. Return the result to the client.

Here is a simplified Route Handler example:

// app/api/chat/route.ts
import OpenAI from "openai";

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

export async function POST(req: Request) {
  const body = await req.json();
  const messages = body.messages ?? [];

  const response = await client.responses.create({
    model: "gpt-4.1-mini",
    input: [
      {
        role: "developer",
        content: "You are a helpful assistant. Keep answers concise.",
      },
      ...messages.map((m: any) => ({
        role: m.role,
        content: m.content,
      })),
    ],
  });

  return Response.json({
    reply: response.output_text,
  });
}

This structure keeps the API key on the server and avoids exposing any provider credentials to the browser. That is essential for security because the browser is an untrusted environment. The OpenAI quickstart also emphasizes using the SDK from server-side code, and the platform docs show that responses can be generated and streamed without pushing secrets to the client. (platform.openai.com)

Prompt handling deserves careful attention even in a Hello World app. A common pattern is to prepend a fixed developer instruction, then append the conversation history. This gives the model a stable behavioral baseline while still preserving context from previous turns. If your app needs special tone, domain knowledge, or formatting, keep those instructions server-side so they cannot be modified from the browser.

If you prefer Server Actions, the same principles apply: keep the secret on the server, validate user input, and never let the client call the provider directly. The main difference is the transport mechanism, not the security model.

7) Streaming Responses: Using Streaming UI Patterns so Users See Tokens as They Arrive

Streaming is the feature that makes chat apps feel alive. Without streaming, every request looks like a full round-trip wait. With streaming, the assistant begins responding almost immediately, and the UI can append tokens as the model generates them. Next.js supports streaming in the app layer through component streaming and route streaming, while the OpenAI Responses API supports server-sent streaming events when enabled. That combination makes it straightforward to implement a responsive chat experience. (nextjs.org)

There are two common approaches:

  • Stream text from the backend and progressively update the UI

  • Use a library or helper that manages streamed message state

For a custom implementation, your API route can return a stream rather than a single JSON blob. The client reads from the stream and updates the current assistant message as chunks arrive. This usually requires a little more code, but it provides the most direct control over rendering and error handling.

A practical UI pattern is:

  1. Add the user message immediately.

  2. Insert an empty assistant message placeholder.

  3. Stream tokens into that placeholder as they arrive.

  4. Mark the message complete when the stream ends.

That pattern gives the user instant feedback and avoids the perception of latency. It also makes retries easier, because the assistant slot already exists in the UI. If the provider sends a partial result and then errors, you can show the partial text with an inline retry option instead of dropping the conversation entirely.

Streaming response lifecycle

Next.js’s own docs emphasize that streaming can break a route into smaller chunks and send them progressively, which is conceptually similar to token streaming in AI apps. In both cases, the goal is the same: reduce time to first meaningful content and keep the interface feeling fluid. (nextjs.org)

For a Hello World build, even if you do not implement full token-by-token rendering, designing your API with streaming in mind will save time later. Avoid hard-coding “single final JSON response only” assumptions into your UI state model.

8) Conversation State and Memory: Preserving History, Trimming Context, and Handling Retries/Errors

Conversation memory is deceptively simple. The UI can preserve every visible message, but the model does not need to receive every message forever. Token limits, latency, and cost all make it important to trim or summarize context over time. In a small demo, you can send the full transcript. In a real app, you usually want a policy that keeps recent turns and compresses older ones. That helps control API costs and keeps the request payload manageable.

A common memory strategy is:

  • Keep the last N turns verbatim.

  • Store an optional summary of older conversation.

  • Rebuild the prompt from summary + recent messages.

  • Reset or summarize when the thread gets too long.

This balances continuity and efficiency. It also improves reliability because long, noisy contexts can increase response latency and make model behavior less predictable. If the app is a support assistant or onboarding bot, context trimming becomes especially important as the user spends more time in a single thread.

Retries matter too. Network failures, timeouts, and provider errors are normal. Your frontend should be able to distinguish among:

  • validation errors, such as empty input

  • request errors, such as a malformed payload

  • provider errors, such as rate limits or upstream failures

  • streaming interruptions, where partial output exists

A good pattern is to keep the failed message in the UI with a retry button. On retry, you can resend the last user turn and regenerate only the assistant response. If you store messages in a database, you can also mark request IDs and deduplicate repeated submissions.

From a model API perspective, state handling should be deliberate. OpenAI’s guidance around the Responses API highlights that it is designed for multi-turn interactions and can preserve reasoning-related state across turns and tool calls in supported modes. Even if your Hello World app does not use those advanced features, the general design lesson is important: conversation history is part of the product contract, not just a frontend convenience. (cdn.openai.com)

9) Production Considerations: Rate Limits, Moderation, Logging, Observability, and Deployment

Once your Hello World app is working, production concerns quickly become the real work. Rate limiting is the first issue to address. Without it, a single user or bot can drive up costs and degrade latency for everyone else. You should limit requests per IP, per user, or per account tier depending on your product model. This is especially important for public demo apps.

Moderation is another critical layer. OpenAI’s agent-building guidance explicitly references using the moderation API to vet user inputs, which reflects a broader best practice: user-generated prompts should be screened before they reach your model or downstream tools. Even a simple chat app can encounter abuse, so it is wise to build moderation into the request path early. (cdn.openai.com)

Logging and observability should capture enough detail to debug without over-collecting sensitive data. At minimum, log:

  • request ID

  • user/session ID

  • model name

  • latency

  • token usage if available

  • error category

  • retry count

Do not log API keys, full secrets, or unnecessary personal data. If your app stores transcripts, define retention rules and access controls from the start. For streaming apps, instrument both time-to-first-token and total completion time, because those metrics reveal different bottlenecks.

Deployment is simpler with Next.js because the same app can host frontend and backend logic. Platforms such as Vercel are common choices for this architecture, but the important part is that your deployment environment supports serverless or edge-compatible route handling, environment variables, and streaming responses. Make sure your production environment sets OPENAI_API_KEY securely and that your build does not accidentally expose it in the client bundle.

A production-ready checklist should also include:

  • request validation

  • output length caps

  • abuse detection

  • timeout handling

  • fallback error messages

  • observability dashboards

  • storage policy for chat transcripts

Even if your initial app is tiny, these guardrails will help you avoid the common “works in local dev, fails in production” trap.

10) SEO and Content Strategy: Internal Linking, Schema, Performance, and a Beginner-Friendly Launch Checklist

If your AI chat app is public-facing, SEO and content strategy matter more than many developers expect. Even a simple product page can benefit from structured internal links, descriptive headings, and fast load times. Next.js is already well positioned here because the App Router supports server rendering, fast navigation, and streaming, which can improve perceived performance and crawlability when used correctly. Next.js also emphasizes linking and navigational optimizations such as prefetching and streaming for dynamic routes. (nextjs.org)

A practical SEO strategy for an AI chat app includes:

  • A landing page explaining what the app does

  • A use-case page for each major audience

  • Internal links to docs, examples, pricing, and support

  • Clear metadata and social share previews

  • FAQ content that answers common setup questions

Schema markup can help search engines understand your product, especially if the app has a marketing site or documentation hub. For a chat product, SoftwareApplication, FAQPage, and article schema are often useful. Keep the prose concise and technical, and make sure the pages answer real developer intent: setup, APIs, streaming, security, and limits.

Performance is also part of content strategy. A fast page is more likely to be read, indexed, and shared. Avoid shipping unnecessary client-side JavaScript on your marketing pages. Let Server Components handle static content where possible, and reserve Client Components for genuinely interactive parts like the chat widget or code sample toggles.

Here is a beginner-friendly launch checklist:

  • Verify the API key is server-only

  • Test the chat flow on mobile and desktop

  • Confirm streaming works under slow network conditions

  • Add loading and error states

  • Cap max input length

  • Log request IDs and failures

  • Add rate limiting

  • Add moderation for user input

  • Write a short getting-started guide

  • Link to docs from the app footer

The goal is not to over-engineer the demo, but to ensure your first public version feels trustworthy, fast, and easy to understand. Good SEO and content structure make the app easier to discover, while solid performance makes it easier to use.

Conclusion

A Hello World AI chat app is the simplest useful expression of a modern AI product, but it touches nearly every important design choice: UI composition, server boundaries, streaming, memory, security, and operational readiness. Next.js is a strong fit because App Router, Server Components, Route Handlers, and streaming let you build a compact full-stack app without fragmenting your codebase. (nextjs.org)

The key takeaway is to treat the demo as a real architecture, not just a toy. Keep secrets on the server, choose an API that streams well, preserve conversation history intentionally, and design the UI around partial output and failure recovery. If you do those things from the start, your “Hello World” app becomes a solid foundation for a production assistant.

References