Xzero UI
Pro library

AI chatPro

A full-screen AI chat where answers stream in as markdown with folding reasoning, tool chips and cited sources. People can switch chats and models, edit or retry a message into versions, rate replies and stop a reply mid-stream.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/ai-chat
Live · keyboard ready

Use it as the main assistant screen of an app. Pass respond(messages, { signal, model }) as an async iterable of events (reasoning, tool, sources, text deltas, error) to stream a real model; Stop aborts the signal. initialMessages, history, models, suggestions, onFeedback, onModelChange and onNewChat wire it to your data. It fills its container; set --chat-height to embed it smaller and --chat-column for the conversation width.

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/ai-chat

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { AiChat } from "@/components/xzero/blocks/ai-chat/ai-chat";
import type { ChatRespond } from "@/components/xzero/blocks/ai-chat/ai-chat";

// Map your API's stream onto chat events. Text can arrive in any chunk size; the chat paces it.
const respond: ChatRespond = async function* (messages, { signal, model }) {
  const response = await fetch("/api/chat", {
    method: "POST",
    body: JSON.stringify({ model: model.id, messages: messages.map(({ role, content }) => ({ role, content })) }),
    signal,
  });
  const reader = response.body!.pipeThrough(new TextDecoderStream()).getReader();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    yield { type: "text", delta: value };
  }
};

export function SupportChat() {
  return <AiChat respond={respond} onFeedback={(message, value) => track("reply_feedback", { id: message.id, value })} />;
}

API reference

AiChat

A focused AI chat card. A slim header holds the conversation title (a menu of recent chats), a model chip with a compact model menu and a New chat button. The thread scrolls inside the card and follows new text while it streams. User turns are quiet right aligned bubbles that can be copied or edited; assistant turns are unboxed, with a small model mark, a "Thought for 4s" line that folds open, tool chips that open their searches, sources or query, and markdown answers with lists, inline code, code blocks with copy and numbered citations that preview their source on hover. Copy, retry and feedback appear on hover, and an edit or retry adds a version shown as a "2 / 2" switcher. The composer autosizes, takes attachments and turns its send button into Stop while a reply streams. Empty, thinking, streaming, stopped and error states are all built in.

PropTypeDefaultDescription
respond(messages: AiChatMessage[], options: { signal: AbortSignal; model: ChatModel }) => AsyncIterable<ChatStreamEvent>–Streams one reply for the messages on screen. Yield { type: "reasoning", delta }, { type: "reasoning-end", seconds? }, { type: "tool", tool } (upserted by id), { type: "sources", sources } and { type: "text", delta }; the reply ends when the iterable finishes. Throw or yield { type: "error", message } to fail it with a Retry. Stop aborts the signal. Without it the chat answers with scripted, simulated replies.
modelsChatModel[]sampleModelsModels in the header menu: { id, name, note?, mark, color? }. The mark is any SVG component; color tints a mono mark. Samples are Claude Opus 5.5, Claude Sonnet 5.5, GPT-6.1 Sol, Gemini 4 Argon and Mistral Large 4.
defaultModelstring–Id of the model selected first. Defaults to the first model.
initialMessagesAiChatMessage[]–The open conversation, alternating user and assistant messages. Assistant messages can carry model, reasoning, thoughtFor, tools, sources and status. A trailing user message is answered when the card first scrolls into view.
titlestring–Title of the open conversation. Defaults to one taken from its first message.
historyAiChatHistoryItem[]–Earlier conversations in the title menu: { id, title, group?, messages }. Each opens settled at its latest message and keeps its own scroll position. Defaults to sample chats only in the simulated preview.
suggestionsChatSuggestion[]sampleSuggestionsUp to three prompts in the empty state: { label, prompt?, icon? } where icon is "database", "timer", "activity" or "search".
greetingstring"What are we working on?"Heading of the empty state.
placeholderstring"Ask anything"Composer placeholder.
autoPlayboolean–Plays a two turn scripted conversation once the card is in view. Defaults to true only without respond and initialMessages. Typing, sending or switching chats stops it.
onFeedback(message: AiChatMessage, value: "up" | "down" | null) => void–Called when a reply is rated or the rating is cleared. Without it the preview records the rating locally and announces it as simulated.
onModelChange(model: ChatModel) => void–Called when a model is picked. New replies use it; earlier replies keep the mark of the model that wrote them.
onNewChat() => void–Called by the New chat button and menu item.
classNamestring–Class on the root. It fills its container's width and the screen's height; set --chat-height to fit a smaller area and --chat-column (default 760px) for the width of the conversation column.

Keyboard interactions

KeysAction
EnterIn the composer, sends the message. In the edit field, sends the edited message as a new version.
Shift + EnterAdds a new line in the composer or the edit field.
EscapeIn the composer, stops a streaming reply. In the edit field, cancels the edit. In a menu, closes it and returns focus to its chip.
TabMoves through the chat menu, model chip, New chat, each message's actions, reasoning and tool toggles, citations and the composer controls.
Arrow up / Arrow downMoves through the chat and model menus; Enter or Space picks the highlighted row.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Guidance for AI tools

Blocks are complete, self-contained screens with sample data. Replace the sample data and connect the callbacks described above. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components