Chat
Chat composes conversational turns with message bubbles, reasoning traces, tool calls, and live response states.
@duskmoon-dev/components/chatUsage
When to use
- Use it to clarify state, metadata, content hierarchy, or loading without creating a new workflow.
- Pair it with semantic content so visual treatment never becomes the only source of meaning.
Implementation notes
Features
Focuses on status, metadata, empty/loading states, or content grouping without owning application state.
Uses DuskMoon tokens so the component follows the active docs theme.
Chat is most often configured through `placement`, `color`, `size`, `filled`.
Renders a start-aligned turn with structural slots
Feature Demos
Feature demos are authored for the component page, then supplemented with behavior scenarios from the component test coverage.
Colors
Chat supports the full semantic color palette: primary, secondary, tertiary, accent, neutral, base, info, success, warning, error.
import "@duskmoon-dev/components/styles.css";
import { Chat } from "@duskmoon-dev/components/chat";
const colors = [
"primary",
"secondary",
"tertiary",
"accent",
"neutral",
"base",
"info",
"success",
"warning",
"error"
] as const;
export function ChatColorsDemo() {
return (<div style={{ display: "grid", gap: 10 }}>
{colors.map((color) => (
<Chat key={color}>
<Chat.Bubble color={color}>{color} response</Chat.Bubble>
</Chat>
))}
</div>);
}Thinking (2s)
search_componentsDone
{"query":"chat"}Found the DuskMoon Chat primitives.
Basic usage
Import the component stylesheet and Chat from its package subpath, then render it with the core props.
import "@duskmoon-dev/components/styles.css";
import { Chat } from "@duskmoon-dev/components/chat";
export function Example() {
return (<Chat aria-live="polite">
<Chat.Avatar>
<span className="avatar avatar-sm avatar-info">AI</span>
</Chat.Avatar>
<Chat.Header>Assistant · just now</Chat.Header>
<Chat.Reasoning open>
<summary>Thinking (2s)</summary>
<div>Reviewing the component API before answering.</div>
</Chat.Reasoning>
<Chat.Tool status="success" open>
<Chat.ToolHeader>
<span>search_components</span>
<Chat.ToolStatus>Done</Chat.ToolStatus>
</Chat.ToolHeader>
<Chat.ToolCall>{'{"query":"chat"}'}</Chat.ToolCall>
<Chat.ToolResult>Found the DuskMoon Chat primitives.</Chat.ToolResult>
</Chat.Tool>
<Chat.Bubble color="primary" streaming>
Chat is ready for React.
</Chat.Bubble>
<Chat.Footer>Delivered</Chat.Footer>
</Chat>);
}Thinking (2s)
search_componentsDone
{"query":"chat"}Found the DuskMoon Chat primitives.
Renders a start-aligned turn with structural slots
test-backedChat scenario from the component test coverage: renders a start-aligned turn with structural slots.
import "@duskmoon-dev/components/styles.css";
import { Chat } from "@duskmoon-dev/components/chat";
export function ChatRendersAStartAlignedDemo() {
return (<Chat aria-live="polite">
<Chat.Avatar>
<span className="avatar avatar-sm avatar-info">AI</span>
</Chat.Avatar>
<Chat.Header>Assistant · just now</Chat.Header>
<Chat.Reasoning open>
<summary>Thinking (2s)</summary>
<div>Reviewing the component API before answering.</div>
</Chat.Reasoning>
<Chat.Tool status="success" open>
<Chat.ToolHeader>
<span>search_components</span>
<Chat.ToolStatus>Done</Chat.ToolStatus>
</Chat.ToolHeader>
<Chat.ToolCall>{'{"query":"chat"}'}</Chat.ToolCall>
<Chat.ToolResult>Found the DuskMoon Chat primitives.</Chat.ToolResult>
</Chat.Tool>
<Chat.Bubble color="primary" streaming>
Chat is ready for React.
</Chat.Bubble>
<Chat.Footer>Delivered</Chat.Footer>
</Chat>);
}Thinking (2s)
search_componentsDone
{"query":"chat"}Found the DuskMoon Chat primitives.
Supports end placement and bubble variants
test-backedChat scenario from the component test coverage: supports end placement and bubble variants.
import "@duskmoon-dev/components/styles.css";
import { Chat } from "@duskmoon-dev/components/chat";
export function ChatSupportsEndPlacementAndDemo() {
return (<Chat aria-live="polite">
<Chat.Avatar>
<span className="avatar avatar-sm avatar-info">AI</span>
</Chat.Avatar>
<Chat.Header>Assistant · just now</Chat.Header>
<Chat.Reasoning open>
<summary>Thinking (2s)</summary>
<div>Reviewing the component API before answering.</div>
</Chat.Reasoning>
<Chat.Tool status="success" open>
<Chat.ToolHeader>
<span>search_components</span>
<Chat.ToolStatus>Done</Chat.ToolStatus>
</Chat.ToolHeader>
<Chat.ToolCall>{'{"query":"chat"}'}</Chat.ToolCall>
<Chat.ToolResult>Found the DuskMoon Chat primitives.</Chat.ToolResult>
</Chat.Tool>
<Chat.Bubble color="primary" streaming>
Chat is ready for React.
</Chat.Bubble>
<Chat.Footer>Delivered</Chat.Footer>
</Chat>);
}Thinking (2s)
search_componentsDone
{"query":"chat"}Found the DuskMoon Chat primitives.
Maps every bubble size and tool state
test-backedChat scenario from the component test coverage: maps every bubble size and tool state.
import "@duskmoon-dev/components/styles.css";
import { Chat } from "@duskmoon-dev/components/chat";
export function ChatMapsEveryBubbleSizeDemo() {
return (<Chat aria-live="polite">
<Chat.Avatar>
<span className="avatar avatar-sm avatar-info">AI</span>
</Chat.Avatar>
<Chat.Header>Assistant · just now</Chat.Header>
<Chat.Reasoning open>
<summary>Thinking (2s)</summary>
<div>Reviewing the component API before answering.</div>
</Chat.Reasoning>
<Chat.Tool status="success" open>
<Chat.ToolHeader>
<span>search_components</span>
<Chat.ToolStatus>Done</Chat.ToolStatus>
</Chat.ToolHeader>
<Chat.ToolCall>{'{"query":"chat"}'}</Chat.ToolCall>
<Chat.ToolResult>Found the DuskMoon Chat primitives.</Chat.ToolResult>
</Chat.Tool>
<Chat.Bubble color="primary" streaming>
Chat is ready for React.
</Chat.Bubble>
<Chat.Footer>Delivered</Chat.Footer>
</Chat>);
}Thinking (2s)
search_componentsDone
{"query":"chat"}Found the DuskMoon Chat primitives.
Uses native details and summary elements for LLM blocks
test-backedChat scenario from the component test coverage: uses native details and summary elements for llm blocks.
import "@duskmoon-dev/components/styles.css";
import { Chat } from "@duskmoon-dev/components/chat";
export function ChatUsesNativeDetailsAndDemo() {
return (<Chat aria-live="polite">
<Chat.Avatar>
<span className="avatar avatar-sm avatar-info">AI</span>
</Chat.Avatar>
<Chat.Header>Assistant · just now</Chat.Header>
<Chat.Reasoning open>
<summary>Thinking (2s)</summary>
<div>Reviewing the component API before answering.</div>
</Chat.Reasoning>
<Chat.Tool status="success" open>
<Chat.ToolHeader>
<span>search_components</span>
<Chat.ToolStatus>Done</Chat.ToolStatus>
</Chat.ToolHeader>
<Chat.ToolCall>{'{"query":"chat"}'}</Chat.ToolCall>
<Chat.ToolResult>Found the DuskMoon Chat primitives.</Chat.ToolResult>
</Chat.Tool>
<Chat.Bubble color="primary" streaming>
Chat is ready for React.
</Chat.Bubble>
<Chat.Footer>Delivered</Chat.Footer>
</Chat>);
}Thinking (2s)
search_componentsDone
{"query":"chat"}Found the DuskMoon Chat primitives.
Renders an accessible typing indicator with the required inner dot
test-backedChat scenario from the component test coverage: renders an accessible typing indicator with the required inner dot.
import "@duskmoon-dev/components/styles.css";
import { Chat } from "@duskmoon-dev/components/chat";
export function ChatRendersAnAccessibleTypingDemo() {
return (<Chat aria-live="polite">
<Chat.Avatar>
<span className="avatar avatar-sm avatar-info">AI</span>
</Chat.Avatar>
<Chat.Header>Assistant · just now</Chat.Header>
<Chat.Reasoning open>
<summary>Thinking (2s)</summary>
<div>Reviewing the component API before answering.</div>
</Chat.Reasoning>
<Chat.Tool status="success" open>
<Chat.ToolHeader>
<span>search_components</span>
<Chat.ToolStatus>Done</Chat.ToolStatus>
</Chat.ToolHeader>
<Chat.ToolCall>{'{"query":"chat"}'}</Chat.ToolCall>
<Chat.ToolResult>Found the DuskMoon Chat primitives.</Chat.ToolResult>
</Chat.Tool>
<Chat.Bubble color="primary" streaming>
Chat is ready for React.
</Chat.Bubble>
<Chat.Footer>Delivered</Chat.Footer>
</Chat>);
}Thinking (2s)
search_componentsDone
{"query":"chat"}Found the DuskMoon Chat primitives.
Exposes named primitives through the compound API
test-backedChat scenario from the component test coverage: exposes named primitives through the compound api.
import "@duskmoon-dev/components/styles.css";
import { Chat } from "@duskmoon-dev/components/chat";
export function ChatExposesNamedPrimitivesThroughDemo() {
return (<Chat aria-live="polite">
<Chat.Avatar>
<span className="avatar avatar-sm avatar-info">AI</span>
</Chat.Avatar>
<Chat.Header>Assistant · just now</Chat.Header>
<Chat.Reasoning open>
<summary>Thinking (2s)</summary>
<div>Reviewing the component API before answering.</div>
</Chat.Reasoning>
<Chat.Tool status="success" open>
<Chat.ToolHeader>
<span>search_components</span>
<Chat.ToolStatus>Done</Chat.ToolStatus>
</Chat.ToolHeader>
<Chat.ToolCall>{'{"query":"chat"}'}</Chat.ToolCall>
<Chat.ToolResult>Found the DuskMoon Chat primitives.</Chat.ToolResult>
</Chat.Tool>
<Chat.Bubble color="primary" streaming>
Chat is ready for React.
</Chat.Bubble>
<Chat.Footer>Delivered</Chat.Footer>
</Chat>);
}Thinking (2s)
search_componentsDone
{"query":"chat"}Found the DuskMoon Chat primitives.
Theme aware
Docs previews inherit the DuskMoon data-theme value. Use the header switch to compare light and dark rendering.
<div data-theme="sunshine">
<Chat aria-live="polite">
<Chat.Avatar>
<span className="avatar avatar-sm avatar-info">AI</span>
</Chat.Avatar>
<Chat.Header>Assistant · just now</Chat.Header>
<Chat.Reasoning open>
<summary>Thinking (2s)</summary>
<div>Reviewing the component API before answering.</div>
</Chat.Reasoning>
<Chat.Tool status="success" open>
<Chat.ToolHeader>
<span>search_components</span>
<Chat.ToolStatus>Done</Chat.ToolStatus>
</Chat.ToolHeader>
<Chat.ToolCall>{'{"query":"chat"}'}</Chat.ToolCall>
<Chat.ToolResult>Found the DuskMoon Chat primitives.</Chat.ToolResult>
</Chat.Tool>
<Chat.Bubble color="primary" streaming>
Chat is ready for React.
</Chat.Bubble>
<Chat.Footer>Delivered</Chat.Footer>
</Chat>
</div>
<div data-theme="moonlight">
<Chat aria-live="polite">
<Chat.Avatar>
<span className="avatar avatar-sm avatar-info">AI</span>
</Chat.Avatar>
<Chat.Header>Assistant · just now</Chat.Header>
<Chat.Reasoning open>
<summary>Thinking (2s)</summary>
<div>Reviewing the component API before answering.</div>
</Chat.Reasoning>
<Chat.Tool status="success" open>
<Chat.ToolHeader>
<span>search_components</span>
<Chat.ToolStatus>Done</Chat.ToolStatus>
</Chat.ToolHeader>
<Chat.ToolCall>{'{"query":"chat"}'}</Chat.ToolCall>
<Chat.ToolResult>Found the DuskMoon Chat primitives.</Chat.ToolResult>
</Chat.Tool>
<Chat.Bubble color="primary" streaming>
Chat is ready for React.
</Chat.Bubble>
<Chat.Footer>Delivered</Chat.Footer>
</Chat>
</div>API
The API reference below lists every parsed exported type or interface for Chat. Start with `placement`, `color`, `size`, `filled` for common usage.
packages/components/src/components/chat/Chat.types.tsScenarios: packages/components/src/components/chat/Chat.test.tsx| Prop | Type | Required | Description |
|---|---|---|---|
placement | ChatPlacement | No | Position the turn at the start or end of the conversation row. |
| Prop | Type | Required | Description |
|---|---|---|---|
color | ChatBubbleColor | No | Apply a semantic DuskMoon color to the bubble. |
size | ChatBubbleSize | No | Control the bubble padding and font size. @default "md" |
filled | boolean | No | Use the saturated form of the selected color. |
streaming | boolean | No | Show the live streaming caret after the bubble content. |
| Prop | Type | Required | Description |
|---|---|---|---|
status | ChatToolState | No | Reflect the current tool-call state. |
"start" | "end"| "primary" | "secondary" | "tertiary" | "accent" | "neutral" | "base" | "info" | "success" | "warning" | "error""xs" | "sm" | "md" | "lg""pending" | "running" | "success" | "error"ComponentProps<"div">ComponentProps<"div">ComponentProps<"div">ComponentProps<"details">ComponentProps<"summary">ComponentProps<"span">ComponentProps<"div">ComponentProps<"div">Omit<ComponentProps<"span">, "children">ChatPartComponent<HTMLDivElement, ChatProps> & { Avatar: ChatPartComponent<HTMLDivElement, ChatAvatarProps>