A complete chat, ready to skin: replies that stream in, a box to write in, and a list of past conversations. Full page like a chatbot, or a side panel for an in-app copilot.
pnpm dlx shadcn@latest add @latheui/chat-workspacePart of Lathe Pro. Set up your project once with your license key: npx latheui init --key <key>. See Lathe Pro.
Chat Workspace is part of Lathe Pro
The preview is live and free to try. The source, the install and every assembly come with Pro: $79 once, with lifetime updates.
Depends on
Also accepts native section attributes. Give it a height; it fills it.
| Prop | Type | Default | Description |
|---|---|---|---|
| layout | "page" | "panel" | "page" | A full screen with a conversation list, or a narrow side panel. |
| respond* (required) | (prompt, history, { signal, model }) => AsyncIterable<string> | Promise<string> | — | Produces a reply. Yield text to stream it, or return it whole. Stop aborts signal. |
| title | string | "Assistant" | Names the chat and the assistant. |
| subtitle | string | "Typically replies instantly" | Under the title, in the panel. |
| greeting | string | "What can I help with?" | Heading of an empty chat. |
| suggestions | string[] | [] | Prompts offered on an empty chat; pressing one sends it. |
| models | { id; label; description? }[] | — | Shows a model picker. The choice is passed to respond. |
| thinkingLabel | string | "Thinking" | Shown until the first words of a reply arrive. |
| avatar | ReactNode | false | — | The assistant's picture on each reply and in the panel header. |
| onFeedback | (message, feedback) => void | — | Called when a reply is rated. |
| onClose | () => void | — | Shows a close button in the panel. |
| placeholder | string | — | Placeholder of the prompt box. |
| defaultConversations | { id; title; messages }[] | [] | Saved conversations listed on first render. |
| conversationActions | { id; label; icon?; destructive?; onSelect(conversation) }[] | [] | Extra entries in each conversation's menu, between Rename and Delete. |
| onRenameConversation | (conversation, title) => void | — | Called after a conversation is renamed. |
| onDeleteConversation | (conversation) => void | — | Called after a conversation is deleted. |
<ChatWorkspace
respond={respond}
conversationActions={[
{ id: "share", label: "Share", icon: <ShareIcon />, onSelect: (chat) => share(chat.id) },
{ id: "archive", label: "Archive", icon: <ArchiveIcon />, onSelect: (chat) => archive(chat.id) },
]}
onDeleteConversation={(chat) => deleteChat(chat.id)}
/><ChatWorkspace
layout="panel"
respond={async (prompt) => {
const response = await fetch("/api/answer", { method: "POST", body: prompt })
return response.text()
}}
/>