Prodular UI

AI chat

AIPro

A complete streaming chat on the Vercel AI SDK: useChat, reasoning, tool calls, approvals, sources, attachments and a model picker, with a Claude route handler that installs with it.

npx prodular-ui add ai-chat

Part of Prodular UI Pro. Preview and read the docs freely, install with a license. See pricing

Streaming chat

Pick a suggestion. The answer streams reasoning, a ledger lookup, a table with citations and sources, then asks before it emails a customer. This page uses a scripted route so it runs without a key; the next section shows how to point it at Claude.

Relay CopilotReceivables

Ask about receivables

Relay Copilot reads your invoices and customers, and asks before it emails anyone.

Copilot can make mistakes. Check figures before you share them.

Connect to Claude

The install adds a route handler for Claude Opus 5.5. Add your key and drop the demo api prop.

  1. 1

    Install the component and the route

    Adds AiChat and AiMessageParts, the route handler at app/api/chat/route.ts, example tools in lib/ai/tools.ts, and the ai, @ai-sdk/react, @ai-sdk/anthropic and zod packages.

    Terminal
    npx prodular-ui add ai-chat
  2. 2

    Add your Anthropic key

    Create a key in the Claude Console. The route reads it on the server, it never reaches the browser. TOOL_APPROVAL_SECRET is optional and signs approvals in production.

    .env.local
    ANTHROPIC_API_KEY=sk-ant-...
    TOOL_APPROVAL_SECRET=a-long-random-string
  3. 3

    Render the chat

    Without api, AiChat posts to /api/chat, which streams Claude Opus 5.5 with reasoning, tools, approvals and web sources. The demo above passes api="/api/demo-chat", a docs only route that replays a scripted answer with the AI SDK's simulated stream so it runs without a key. Drop that prop and the same UI talks to Claude.

    app/assistant/page.tsx
    import { AiChat } from "@/components/ui/ai-chat"
    
    export default function AssistantPage() {
      return (
        <div className="h-dvh">
          <AiChat
            assistantName="Relay Copilot"
            suggestions={["Which overdue invoices should I chase first?"]}
          />
        </div>
      )
    }
  4. 4

    Make the tools yours

    Swap the sample ledger in lib/ai/tools.ts for your database. Anything that writes, sends or pays goes in toolApproval as "user-approval" in the route, and the chat asks a person before it runs.

    app/api/chat/route.ts
    toolApproval: {
      sendReminder: { type: "user-approval", reason: "Emails a customer on behalf of Halcyon Labs." },
    },

Anatomy

// app/assistant/page.tsx<div className="h-dvh">  <AiChat    assistantName="Relay Copilot"    models={models}    tools={{      sendReminder: {        title: "Send payment reminder",        icon: <MailIcon />,        approval: { actionLabel: "Send reminder", risk: "medium" },      },    }}    suggestions={["Which overdue invoices should I chase first?"]}  /></div>

Guidance

Use it when

  • As the whole assistant surface of a page or panel: it owns the thread, the composer, streaming state, stop, retry and approvals.
  • With the route it installs. app/api/chat/route.ts streams Claude with adaptive thinking, tools, approvals and web sources in the format AiChat reads.
  • When tools have side effects. Mark them user-approval on the server and AiChat shows an approval card before they run.

Avoid

  • Wrapping AiChat in a card inside another card. Give it a full height parent and let the page provide the frame.
  • Building your own message list around it. Use AiMessageParts with useChat when you need a different layout.
  • Calling Anthropic from the browser or passing an API key as a prop. Keys stay in the route handler.

Rules for AI agents

Published in llms-full.txt, the registry and the MCP server, so coding agents follow them when they build with this component.

  • Install with npx prodular-ui add ai-chat. It writes app/api/chat/route.ts and lib/ai/tools.ts. Tell the user to set ANTHROPIC_API_KEY in .env.local; never hard code a key and never read it in a client component.
  • Give AiChat a parent with a definite height (h-dvh, flex-1 with min-h-0, or a fixed height). It fills the parent and scrolls its own thread.
  • Leave api unset to use /api/chat. Only the docs use /api/demo-chat; never ship that route or point production code at it.
  • Keep the default model claude-opus-5-5. If you pass models, every id must also be listed in MODELS in the route, or the route falls back to the default.
  • Define tools on the server in lib/ai/tools.ts. Any tool that writes, sends, pays or deletes must be listed in toolApproval with type user-approval in the route. Never rely on the client to ask.
  • Pass tools on AiChat for every server tool: a verb first title (Send payment reminder), an icon, and approval copy with an actionLabel that names the action. Never show raw tool names.
  • Set TOOL_APPROVAL_SECRET in production so approvals cannot be forged by a modified client.
  • Persist threads by passing id and initialMessages, and save messages in the route with toUIMessageStream onEnd. Do not store chat history in localStorage.
  • Keep sendReasoning and sendSources on in the route and thinking display summarized. Without them the reasoning and sources parts never reach the UI.

API

AiChat

PropTypeDefaultDescription
apistring"/api/chat"Route that returns a UI message stream.
id / initialMessagesstring / UIMessage[]Load a saved thread. The id is sent with every request.
modelsModelOption[]Shows a model picker. The chosen id is sent as body.model.
model / defaultModel / onModelChangestringControl the selected model. Defaults to the first option.
modelPickerReactNodeReplaces the built in picker in the composer toolbar.
toolsRecord<string, AiToolConfig>Title, icon, summary and approval copy per tool name. See AiMessageParts.
suggestionsstring[]Prompts shown while the thread is empty. Clicking one sends it.
assistantNamestring"Assistant"Shown in message headers and on approval cards.
emptyTitle / emptyDescriptionReactNodeCopy for the empty thread.
attachments / acceptboolean / stringtrueLets people attach images, PDFs and text files. They are sent as file parts.
body / headersobjectExtra fields and headers sent with every request, read fresh each time.
onFinish / onErrorfunctionPassed to useChat. onFinish receives isAbort, isError and the final message.
disclaimerReactNodeOne line under the composer.

Installation

The Prodular UI CLI adds the source to components/ui with everything it depends on. In a project that already uses shadcn/ui, the shadcn CLI installs it from the Prodular registry too. See Installation for the first setup.

npx prodular-ui add ai-chat# or, with the registry in components.jsonnpx shadcn@latest add @prodular/ai-chat