tsimport {XDSChatMessageBubble} from '@xds/core/Chat'
| 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. |
tsx'use client';import {XDSChatMessageList,XDSChatMessage,XDSChatMessageBubble,} from '@xds/core/Chat';import {XDSText} from '@xds/core/Text';import {XDSVStack} from '@xds/core/Layout';const DENSITIES = [{density: 'compact' as const, label: 'Compact'},{density: 'balanced' as const, label: 'Balanced'},{density: 'spacious' as const, label: 'Spacious'},];export default function ChatMessageBubbleDensity() {return (<XDSVStack gap={5}>{DENSITIES.map(({density, label}) => (<XDSVStack key={density} gap={1}><XDSText type="supporting" color="secondary">{label}</XDSText><XDSChatMessageList density={density}><XDSChatMessage sender="assistant"><XDSChatMessageBubble>The build completed in 4.2 seconds.</XDSChatMessageBubble></XDSChatMessage><XDSChatMessage sender="user"><XDSChatMessageBubble>Ship it to staging.</XDSChatMessageBubble></XDSChatMessage></XDSChatMessageList></XDSVStack>))}</XDSVStack>);}
tsx'use client';import {XDSChatMessageList,XDSChatMessage,XDSChatMessageBubble,XDSChatMessageMetadata,} from '@xds/core/Chat';import {XDSAvatar} from '@xds/core/Avatar';import {XDSTimestamp} from '@xds/core/Timestamp';import {XDSText} from '@xds/core/Text';import {XDSVStack} from '@xds/core/Layout';export default function ChatMessageBubbleGrouping() {return (<XDSVStack gap={4}><XDSText type="supporting" color="secondary">Grouped bubbles with tightened sender-side corners</XDSText><XDSChatMessageList><XDSChatMessagesender="assistant"avatar={<XDSAvatar name="Agent" size="small" />}><XDSChatMessageBubblegroup="first"name={<XDSText type="supporting" weight="semibold" color="secondary">Agent</XDSText>}>I reviewed the three files you shared.</XDSChatMessageBubble><XDSChatMessageBubble group="middle">The data model looks solid, but the API handler has a racecondition on concurrent writes.</XDSChatMessageBubble><XDSChatMessageBubblegroup="last"metadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-04-10T10:45:00" format="time" />}/>}>I can draft a fix if you want.</XDSChatMessageBubble></XDSChatMessage><XDSChatMessage sender="user"><XDSChatMessageBubble group="first">Yes please!</XDSChatMessageBubble><XDSChatMessageBubblegroup="last"metadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-04-10T10:46:00" format="time" />}status="delivered"/>}>Also add a test for the concurrent case.</XDSChatMessageBubble></XDSChatMessage></XDSChatMessageList></XDSVStack>);}
tsx'use client';import {XDSChatMessageList,XDSChatMessage,XDSChatMessageBubble,XDSChatMessageMetadata,} from '@xds/core/Chat';import {XDSAvatar} from '@xds/core/Avatar';import {XDSTimestamp} from '@xds/core/Timestamp';import {XDSText} from '@xds/core/Text';import {XDSButton} from '@xds/core/Button';import {XDSIcon} from '@xds/core/Icon';import {XDSHStack, XDSVStack} from '@xds/core/Layout';export default function ChatMessageBubbleMetadata() {return (<XDSVStack gap={4}><XDSText type="supporting" color="secondary">Name on first bubble, metadata on last</XDSText><XDSChatMessageList><XDSChatMessagesender="assistant"avatar={<XDSAvatar name="Agent" size="small" />}><XDSChatMessageBubblename={<XDSText type="supporting" weight="semibold" color="secondary">Agent</XDSText>}metadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-04-10T09:15:00" format="time" />}footer={<XDSHStack gap={1}><XDSButtonlabel="Copy"variant="ghost"size="sm"icon={<XDSIcon icon="copy" size="sm" />}isIconOnlyonClick={() => {}}/><XDSText type="supporting" color="secondary">Claude Opus 4.6</XDSText></XDSHStack>}/>}>Your deployment finished successfully. All 14 checks passed.</XDSChatMessageBubble></XDSChatMessage><XDSChatMessage sender="user"><XDSChatMessageBubblemetadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-04-10T09:16:00" format="time" />}status="read"/>}>Great, can you send me the production URL?</XDSChatMessageBubble></XDSChatMessage></XDSChatMessageList></XDSVStack>);}
tsx'use client';import {XDSChatMessageList,XDSChatMessage,XDSChatMessageBubble,} from '@xds/core/Chat';import {XDSStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';export default function ChatMessageBubbleVariants() {return (<XDSStack direction="vertical" gap={4}><XDSStack direction="vertical" gap={1}><XDSText type="supporting" color="secondary">Filled — sender-colored background (default)</XDSText><XDSChatMessageList><XDSChatMessage sender="user"><XDSChatMessageBubble>Can you summarize the latest deployment logs?</XDSChatMessageBubble></XDSChatMessage></XDSChatMessageList></XDSStack><XDSStack direction="vertical" gap={1}><XDSText type="supporting" color="secondary">Ghost — transparent background, keeps alignment padding</XDSText><XDSChatMessageList><XDSChatMessage sender="assistant"><XDSChatMessageBubble variant="ghost">The last deploy completed at 2:41 PM with zero errors across allthree regions.</XDSChatMessageBubble></XDSChatMessage></XDSChatMessageList></XDSStack></XDSStack>);}
tsx'use client';import {XDSChatMessageList,XDSChatMessage,XDSChatMessageBubble,XDSChatMessageMetadata,} from '@xds/core/Chat';import {XDSTimestamp} from '@xds/core/Timestamp';import * as stylex from '@stylexjs/stylex';const styles = stylex.create({root: {maxWidth: 600,},});export default function ChatMessageBubbleShowcase() {return (<div {...stylex.props(styles.root)}><XDSChatMessageList><XDSChatMessage sender="user"><XDSChatMessageBubble group="first">I just pushed the latest changes to the feature branch.</XDSChatMessageBubble><XDSChatMessageBubblegroup="last"metadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-04-10T09:15:00" format="time" />}status="read"/>}>Can you review when you get a chance?</XDSChatMessageBubble></XDSChatMessage><XDSChatMessage sender="assistant"><XDSChatMessageBubblevariant="ghost"metadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-04-10T09:16:00" format="time" />}/>}>The changes look great — clean code, good test coverage. Ship it!</XDSChatMessageBubble></XDSChatMessage></XDSChatMessageList></div>);}