XDSChatSendButton@xds/core · Chat
Preview coming soon
Usage
XDSChatMessageList is the scrollable container for chat messages. It renders children in a flex column with role="log" for accessibility, provides density context to child messages, and supports infinite scroll for loading older messages. Use it inside XDSChatLayout for full-page chat with auto-scroll and composer docking, or standalone for embedded message panels.Best practices
| Guidance | Practices |
|---|---|
| Do | Compose messages using MessageList > Message > Bubble for consistent sender-aware styling and density. |
| Do | Set the density prop to control spacing globally — compact for sidebars, balanced for most views, spacious for long-form reading. Individual messages can override. |
| Do | Use the group prop on bubbles (first, middle, last) when a single sender sends multiple consecutive messages — it tightens corner radius to visually connect them. |
| Do | Use XDSChatSystemMessage with variant="divider" for date separators and default for inline status notices like joins, leaves, or topic changes. |
| Do | Put name on the first bubble and metadata on the last bubble in a message so they align with the bubble's inline padding. |
| Do | Provide an emptyState prop so new users see a clear prompt to start a conversation instead of a blank screen. |
| Do | Use the ghost bubble variant for AI-style responses that show rich content like code blocks or markdown without a visible boundary. |
| Don't | Don't use XDSChatSystemMessage for sender content — it has no avatar, alignment, or bubble. Use XDSChatMessage with a sender role instead. |
| Don't | Don't put long or multi-line content in a system message — keep it to a single short sentence. If you need more, use a bubble or a card. |
| Don't | Don't nest XDSChatMessage inside another XDSChatMessage — each message is a standalone article element with its own sender context. |
| Don't | Don't apply a fixed height directly on the message list — wrap it in a sized container and let the list fill with flex: 1. |
| Don't | Don't mix filled and ghost bubble variants within the same sender's messages — pick one style per side and use it consistently. |
| Don't | Don't place metadata or names on both the bubble and the message wrapper — pick one based on whether the content has a bubble boundary. |
Anatomy
| Element | Description | |
|---|---|---|
| Message area | required | Scrollable region for messages. Renders children (typically XDSChatMessageList) in a flex column that pushes content to the bottom when the list is short. |
| Frosted glass dock | required | Sticky or fixed container at the bottom with a backdrop-blur layer. Houses the scroll button and composer. |
| Scroll-to-bottom button | Appears when the user scrolls up or new messages arrive. Defaults to XDSChatLayoutScrollButton; pass null to hide or a custom element to override. | |
| Composer | required | The input area for sending messages, typically XDSChatComposer. Docked at the bottom inside the frosted glass layer. |
| Empty state | Centered placeholder shown when no messages exist. Use XDSEmptyState for a consistent look. | |
| Avatar | A sender avatar rendered beside the message. Typically XDSAvatar with size="small". Hidden for system messages. | |
| Name | Sender name above the message body. Place on the bubble when using bubbles, or on the message wrapper for raw content. | |
| Content | required | The message body — one or more XDSChatMessageBubble elements, or any free-form ReactNode like images or tool calls. |
| Metadata | Timestamp, delivery status, and footer actions below the message. Place on the last bubble or on the message wrapper. |
Import
tsimport {XDSChatSendButton} from '@xds/core/Chat'
Props
| Prop | Type | Description |
|---|---|---|
isStreaming | boolean | Whether the assistant is currently streaming. Defaults to context value. |
isDisabled | boolean | Whether the send button is disabled. Defaults to !canSend from context. |
onSend | () => void | Called when the user clicks send. Defaults to context onSubmit. |
onStop | () => void | Called when the user clicks stop during streaming. Defaults to context onStop. |
sendIcon | ReactNode | Custom icon for the send state. Defaults to arrowUp from icon registry. |
stopIcon | ReactNode | Custom icon for the stop state. Defaults to stop from icon registry. |
size | 'sm' | 'md' (default: 'md') | Button size. |
xstyle | StyleXStyles | Additional StyleX styles. |
Examples
Common configurations, variations, and states.ChatSendButton — Custom Icon
tsx'use client';import {XDSChatSendButton} from '@xds/core/Chat';import {XDSIcon} from '@xds/core/Icon';import {XDSStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';import {CheckIcon,PaperAirplaneIcon,SparklesIcon,} from '@heroicons/react/24/solid';import {XCircleIcon} from '@heroicons/react/24/outline';export default function ChatSendButtonCustomIcon() {return (<XDSStack direction="vertical" gap={4}><XDSText type="supporting" color="secondary">Custom icons for send and stop states</XDSText><XDSStack direction="horizontal" gap={4} vAlign="center"><XDSChatSendButtonisDisabled={false}onSend={() => {}}sendIcon={<XDSIcon icon={PaperAirplaneIcon} size="sm" />}/><XDSChatSendButtonisDisabled={false}onSend={() => {}}sendIcon={<XDSIcon icon={CheckIcon} size="sm" />}/><XDSChatSendButtonisDisabled={false}onSend={() => {}}sendIcon={<XDSIcon icon={SparklesIcon} size="sm" />}/><XDSChatSendButtonisStreamingonStop={() => {}}stopIcon={<XDSIcon icon={XCircleIcon} size="sm" />}/></XDSStack></XDSStack>);}
ChatSendButton — In ComposerSend button inside XDSChatComposer, where it reads state from context automatically. No wiring needed — the button enables when the input has content.
tsx'use client';import * as stylex from '@stylexjs/stylex';import {XDSChatComposer} from '@xds/core/Chat';import {XDSStack} from '@xds/core/Layout';const styles = stylex.create({root: {maxWidth: 450,},});export default function ChatSendButtonInComposer() {return (<XDSStack direction="vertical" width="100%" xstyle={styles.root}><XDSChatComposeronSubmit={() => {}}value="Hello, how can you help?"onChange={() => {}}/></XDSStack>);}
ChatSendButton — StatesDisabled, ready, and streaming states at both sizes. The button automatically toggles between send (primary) and stop (secondary) based on streaming state.
tsx'use client';import {XDSChatSendButton} from '@xds/core/Chat';import {XDSStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';export default function ChatSendButtonStates() {return (<XDSStack direction="vertical" gap={2}><XDSText type="supporting" color="secondary">Disabled → Ready → Streaming</XDSText><XDSStack direction="horizontal" gap={3} vAlign="center"><XDSChatSendButton isDisabled onSend={() => {}} /><XDSChatSendButton isDisabled={false} onSend={() => {}} /><XDSChatSendButton isStreaming onStop={() => {}} /></XDSStack></XDSStack>);}
Showcase source
tsx'use client';import {XDSChatSendButton} from '@xds/core/Chat';import {XDSIcon} from '@xds/core/Icon';import {XDSStack} from '@xds/core/Layout';import {SparklesIcon} from '@heroicons/react/24/solid';export default function ChatSendButtonShowcase() {return (<XDSStack direction="horizontal" gap={3} vAlign="center"><XDSChatSendButton isDisabled={false} onSend={() => {}} /><XDSChatSendButtonisDisabled={false}onSend={() => {}}sendIcon={<XDSIcon icon={SparklesIcon} size="sm" />}/><XDSChatSendButton isStreaming onStop={() => {}} /></XDSStack>);}