> ## Documentation Index
> Fetch the complete documentation index at: https://runcrate.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Build an AI Chatbot with Next.js

> Build a streaming AI chatbot using Next.js App Router, the Vercel AI SDK, and Runcrate's DeepSeek-V3 model.

export const RuncrateStyles = () => {
  if (typeof document !== 'undefined' && !document.getElementById('runcrate-overrides')) {
    const s = document.createElement('style');
    s.id = 'runcrate-overrides';
    s.textContent = `
      /* Match Runcrate's rounding scale (--radius: 0.75rem) */
      .rounded-sm { border-radius: 0.5rem !important; }   /* 8px */
      .rounded-md { border-radius: 0.625rem !important; } /* 10px */
      .rounded-lg { border-radius: 0.75rem !important; }  /* 12px */
      .rounded-l-sm { border-top-left-radius: 0.5rem !important; border-bottom-left-radius: 0.5rem !important; }
      .rounded-r-sm { border-top-right-radius: 0.5rem !important; border-bottom-right-radius: 0.5rem !important; }
      .rounded-l-md { border-top-left-radius: 0.625rem !important; border-bottom-left-radius: 0.625rem !important; }
      .rounded-r-md { border-top-right-radius: 0.625rem !important; border-bottom-right-radius: 0.625rem !important; }
      .rounded-l-lg { border-top-left-radius: 0.75rem !important; border-bottom-left-radius: 0.75rem !important; }
      .rounded-r-lg { border-top-right-radius: 0.75rem !important; border-bottom-right-radius: 0.75rem !important; }

      /* Cards: never pure white in light mode */
      .card { background-color: #fcfcfc !important; border-radius: 0.75rem !important; }
      html.dark .card { background-color: #141414 !important; }

      /* Docs hero box */
      .rc-hero { background-color: #fcfcfc; border: 1px solid #e0e0e0; }
      html.dark .rc-hero { background-color: #141414; border-color: #242424; }
      html.dark .rc-hero h1 { color: #f5f5f5; }

      /* Runcrate scrollbar — thin, transparent track, hide-until-hover thumb */
      ::-webkit-scrollbar { width: 6px; height: 6px; background-color: transparent; }
      ::-webkit-scrollbar-track { background-color: transparent; }
      ::-webkit-scrollbar-thumb { background-color: rgba(155, 155, 155, 0.5); border-radius: 10px; transition: opacity 0.3s ease; opacity: 0; }
      ::-webkit-scrollbar-thumb:hover { background-color: rgba(155, 155, 155, 0.7); }
      *:hover::-webkit-scrollbar-thumb,
      *:focus::-webkit-scrollbar-thumb,
      *:active::-webkit-scrollbar-thumb { opacity: 1; }
      * { scrollbar-width: thin; scrollbar-color: rgba(155, 155, 155, 0.5) transparent; }
    `;
    document.head.appendChild(s);
  }
  return null;
};

<RuncrateStyles />

Build a streaming chatbot with Next.js App Router, `@runcrate/ai`, and the Vercel AI SDK's `useChat` hook. Under 50 lines of code for a working chat interface.

## Prerequisites

```bash theme={"theme":"github-dark"}
npx create-next-app@latest my-chatbot --app --typescript --tailwind
cd my-chatbot
npm install @runcrate/ai ai
```

Set your API key in `.env.local`:

```bash theme={"theme":"github-dark"}
RUNCRATE_API_KEY=rc_live_YOUR_API_KEY
```

***

## API route

```typescript theme={"theme":"github-dark"}
// app/api/chat/route.ts
import { runcrate } from '@runcrate/ai';
import { streamText } from 'ai';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: runcrate('deepseek-ai/DeepSeek-V3'),
    system: 'You are a helpful assistant. Be concise and direct.',
    messages,
  });

  return result.toDataStreamResponse();
}
```

***

## Chat component

```tsx theme={"theme":"github-dark"}
// app/page.tsx
'use client';

import { useChat } from '@ai-sdk/react';

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();

  return (
    <div className="mx-auto flex h-screen max-w-2xl flex-col p-4">
      <h1 className="mb-4 text-xl font-medium">AI Chat</h1>

      <div className="flex-1 space-y-4 overflow-y-auto pb-4">
        {messages.map((m) => (
          <div key={m.id} className={m.role === 'user' ? 'text-right' : 'text-left'}>
            <span className="inline-block rounded-lg bg-gray-100 px-3 py-2 dark:bg-gray-800">
              {m.content}
            </span>
          </div>
        ))}
      </div>

      <form onSubmit={handleSubmit} className="flex gap-2">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask anything..."
          className="flex-1 rounded-lg border px-3 py-2"
        />
        <button type="submit" disabled={isLoading}
          className="rounded-lg bg-blue-600 px-4 py-2 text-white disabled:opacity-50">
          Send
        </button>
      </form>
    </div>
  );
}
```

Run `npm run dev` and open `http://localhost:3000`.

***

## Add a system prompt selector

```typescript theme={"theme":"github-dark"}
// app/api/chat/route.ts
import { runcrate } from '@runcrate/ai';
import { streamText } from 'ai';

const PERSONAS: Record<string, string> = {
  default: 'You are a helpful assistant.',
  coding: 'You are a senior software engineer. Give practical code examples.',
  writing: 'You are a writing coach. Help improve clarity and style.',
};

export async function POST(req: Request) {
  const { messages, data } = await req.json();

  const result = streamText({
    model: runcrate('deepseek-ai/DeepSeek-V3'),
    system: PERSONAS[data?.persona] || PERSONAS.default,
    messages,
  });

  return result.toDataStreamResponse();
}
```

***

## Tips

* **DeepSeek-V3** is the recommended default — fast, cheap, strong across general tasks.
* **`useChat`** manages conversation history client-side. Each POST sends the full message array.
* **Error handling**: `useChat` exposes an `error` property you can render in the UI.

## Next steps

* [Vercel AI SDK provider docs](/sdks/vercel-ai)
* [AI Summarization](/examples/ai-summarization) — add summarization to your chat
* [Model catalog](/models/model-catalog) — browse all available models
