AI Chat Page React block for Tailwind CSS v4
v1.0.1Updated in 1.7.0
A React AI chat page with conversation history, a streaming thread with reasoning and tool calls, a prompt composer and plan usage.
The history sits on the left from 1024px (a left drawer behind the menu button below, from 768px with sidebar="open"): New chat (or ⇧⌘O) opens the greeting with six starter cards, the search filters titles, rows group into Pinned, Today, Yesterday and older, and each row's ⋯ menu renames (also F2 or a double click), pins, archives or deletes it after a confirmation. The panel button in the header slides the history away on desktop and the thread glides into the space. Click the title to rename the conversation inline (Enter saves, Escape cancels, an empty title keeps the old one). The model picker sits in the header's center on desktop and inside the composer on phones. Share copies a link with a toast; ⋯ renames, pins, shares, exports the conversation as a Markdown file and deletes it with an Undo toast. Type in the composer and press Enter to send (Shift+Enter for a new line), attach files with + or by dropping them, toggle Web search, Extended thinking and CRM data. The answer arrives as typing dots, then reasoning (Summit Pro or extended thinking), the CRM tool calls one by one and the streamed markdown; Stop or Escape keeps what arrived. Scroll up while it streams and the thread lets go, with a jump button to come back. Under the answer: follow-up rows, copy, regenerate and thumbs; edit a question to ask again. A prompt with the word "error" fails and shows Retry. Switching conversations stops a running answer. The sidebar footer shows the person, the plan and its reset date, a usage ring that opens the four meters and the Upgrade to Business link.
AI Chat Page is a Pro React block in the React blocks category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add ai-chat-page, together with Avatar, Button, Chat Message, Chat Thread, Conversation List and 22 more and the npm packages motion and lucide-react.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add ai-chat-pageAI Chat Page is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import AiChatPage from @/components/blocks/ai-chat-page.
// app/assistant/page.tsx: a standalone app screen (or inside the admin shell with height="fill")
"use client"
import { AiChatPage, type AiChatSendHandler } from "@/components/blocks/ai-chat-page"
// stream the answer from your own route (server-sent text chunks)
const onSend: AiChatSendHandler = async function* (message, { conversationId, model, tools, signal }) {
const response = await fetch("/api/assistant", {
method: "POST",
body: JSON.stringify({ conversationId, model, tools, text: message.text }),
signal,
})
const reader = response.body!.pipeThrough(new TextDecoderStream()).getReader()
while (true) {
const { value, done } = await reader.read()
if (done) break
yield value
}
}
export default function AssistantPage() {
return <AiChatPage onSend={onSend} defaultConversationId={null} />
}Props
conversationsAiConversation[]The history, controlled: ConversationItem (id, title, updatedAt, pinned, unread, archived, model) plus messages: ChatMessageData[] and optional followUps. Pair it with onConversationsChange, which also fires for every streamed chunk.
defaultConversationsAiConversation[]The history while uncontrolled; defaults to AI_CHAT_SAMPLE.conversations (ten sample conversations, "Overdue invoices in September" with its reasoning, tool calls, table and email).
onConversationsChange(conversations: AiConversation[]) => voidCalled with the whole history after every change: a new chat, a message, a streamed chunk, a rename, a pin, an archive or a delete.
conversationIdstring | nullThe open conversation, controlled; null is a new chat.
defaultConversationIddefault"overdue-invoices"string | nullThe conversation open first; null ("new" in the playground) starts on the greeting.
onConversationChange(id: string | null) => voidCalled when a row, New chat, a delete or a first message changes the open conversation.
modelsAiModel[]The models of the pickers; defaults to AI_CHAT_SAMPLE.models (Summit Swift, Standard, Pro, Vision, Ultra locked to Business, Local 8B).
modelstringThe chosen model's id, controlled; pair it with onModelChange.
defaultModeldefault"standard"stringThe model while uncontrolled. Summit Pro answers with a reasoning block first.
onModelChange(model: string) => voidCalled when the header or composer picker picks a model.
toolsPromptTool[]The composer's toggles; defaults to AI_CHAT_SAMPLE.tools (Web search, Extended thinking, CRM data). The mock runs its CRM tool calls only with crm on and thinks first with reasoning on.
defaultActiveToolsdefault"["crm"]"string[]The tools switched on at first.
suggestionsChatSuggestion[]The starter cards of a new chat (two columns from 640px, one below); defaults to AI_CHAT_SAMPLE.suggestions, six sample starters.
usageUsageMeterItem[]The plan's meters behind the sidebar's usage ring; defaults to AI_CHAT_SAMPLE.usage (tokens, Pro messages, images, API cost). [] hides the ring.
planAiChatPlan | null{ name, resetsAt, upgrade: { label, description, href } } for the footer line "Pro · resets on Oct 1, 2026" and the upsell link; defaults to AI_CHAT_SAMPLE.plan, null hides both.
userChatPersonThe signed-in person ({ id, name, avatar }): the greeting's first name, the questions' author and the footer; defaults to Anna Price.
onSend(message: PromptInputMessage, context: AiChatSendContext) => (AsyncIterable<string> | Promise<string>)AiChatSendHandler; the context is { conversationId, model, tools, signal, messages }. Plug a real model here: yield text deltas to stream the answer or resolve with the whole of it; a rejection shows the message's error with Retry. The signal aborts on Stop, on switching conversations and on unmount. Without it a scripted mock streams (reasoning for Summit Pro, CRM tool calls, the canned answers of AI_CHAT_SAMPLE.replies, a generic answer otherwise; a prompt with the word "error" fails).
repliesAiChatCannedReply[]The mock model's scripts ({ id, match: RegExp, reasoning?, toolCalls?, content, followUps? }), tested against the prompt folded to lowercase without diacritics; defaults to AI_CHAT_SAMPLE.replies.
initialPromptdefault""stringSent once, shortly after mount, into the open conversation (a new one when none is open), like a ?q= deep link.
getShareUrl(conversation: AiConversation) => stringThe link Share copies; defaults to origin + /assistant/<id>.
sidebarFooterReactNodeContent under the usage block, in the sidebar and the drawer (a version line, help links).
heightdefault"screen""fill" | "screen"screen is a standalone h-dvh app with its own <main>; fill takes what is left of a shell with layout="fill" (admin-shell) and scrolls inside its panes.
sidebardefault"auto""auto" | "open" | "closed"auto: the history is open from 1024px and a left drawer below; open: inline from 768px (tablets too); closed: starts collapsed on desktop, the header button slides it in.
variantdefault"default""default" | "bubbles" | "flat"The messages' layout, passed to ChatThread: the question in a bubble and the answer as text, both in bubbles, or both on the left with names.
newChatShortcutdefault"shift+mod+o"stringOpens a new chat from anywhere, shown in the New chat tooltip; "" binds nothing.
searchShortcutdefault""stringFocuses the history search from anywhere ("mod+k"); "" binds nothing.
showUsagedefaulttruebooleanThe sidebar footer: the person, the plan line, the usage ring and the upsell.
assistantNamedefault"Summit Assistant"stringThe assistant's name in the sidebar brand, the composer placeholder, the message headers (flat) and screen reader text.
labelsPartial<AiChatPageLabels>Every visible string, English by default (DEFAULT_AI_CHAT_PAGE_LABELS; AI_CHAT_PAGE_LABELS_EN is the same set). Nested keys (list, thread, message, markdown, reasoning, toolCalls, composer, modelPicker, usageMeter, suggestionChips, inlineEdit) translate the composed items.
localedefault"en-US"stringIntl locale for dates, times, token counts and money.
nowDate | numberThe reference time for the history groups and day separators; new messages move on from it. Defaults to the live clock.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"The accent of the send button, the question bubbles, the selected row, the starter card icons and the brand mark.
loadingdefaultfalsebooleanThe history, the title and the thread as skeletons; the composer is disabled.
animateIndefaulttruebooleanThe thread rises in once when the page mounts in the browser; off in thumbnails and under reduced motion.
linkComponentdefault"<a>"(props: LinkComponentProps) => ReactNodeThe router link for the upsell link; defaults to the LinkProvider's (a plain anchor).
classNamestringClasses for the root.
classNamesPartial<Record<AiChatPageSlot, string>>Classes for the parts: sidebar, brand, list, usage, main, header, title, thread, empty, drawer.
Parts
DEFAULT_AI_CHAT_PAGE_LABELS
constantThe English strings for every visible label, the default of labels. Type: AiChatPageLabels.
AI_CHAT_PAGE_LABELS_EN
constantThe same English strings under their older name, kept for existing imports.
AI_CHAT_SAMPLE
constantThe demo data: user, models, tools, suggestions, usage, plan, ten conversations, the mock replies and their defaults.
AI_CHAT_CONVERSATION_IDS
constantThe ids of the sample conversations, for a playground select or a deep link.
AI_CHAT_NOW
constantThe demo clock (Sep 24, 2026, 4:40 PM) as a timestamp, passed as now so history groups stay stable.
findCannedReply
functionThe mock model's matcher: (prompt, replies?) => AiChatCannedReply, the first reply whose match fits the folded prompt, else a generic one.
AiChatPageProps
typeProps of AiChatPage, including the div attributes of the root.
AiChatSendHandler
typeThe onSend signature: (message, context) => AsyncIterable<string> | Promise<string>.
AiConversation
typeA history entry: ConversationItem plus messages and optional followUps.
Examples
New chat
The greeting and six starter cards; pick one and it streams.
Streaming
Summit Pro thinks, then writes a plan with a table and code. Scroll up: it lets go.
Inside admin-shell
height="fill" in the shell's fill layout at /assistant.
Bubbles
variant="bubbles": both sides in bubbles.
Renamed assistant, en-GB
assistantName="Atlas" in the header, the placeholder and the answers, with locale en-GB.
Brand, sidebar closed
tone="brand" on a new chat with the history collapsed.
Loading
The history, the title and the thread as skeletons.
Dependencies
Files
- components/blocks/ai-chat-page.tsx
- components/blocks/ai-chat-page-data.ts
FAQ
Is the React AI Chat Page block free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install AI Chat Page?
Run npx wingo-ui@latest add ai-chat-page. It also adds the Wingo UI items it builds on (Avatar, Button, Chat Message, Chat Thread, Conversation List and 22 more) and the npm packages motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/ai-chat-page.
How does AI Chat Page behave on phones?
The model picker sits in the header's center on desktop and inside the composer on phones.
See also
More React blocks
Articles using this component
- React AI Chat UI: Streaming, Auto-Scroll and Mobile
Building UI with AI agents10 min read