tsimport {XDSChatMessageList} 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 {XDSVStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';import {XDSAvatar} from '@xds/core/Avatar';import * as stylex from '@stylexjs/stylex';const styles = stylex.create({section: {flex: 1,minHeight: 0,},});const DENSITIES = ['compact', 'balanced', 'spacious'] as const;const AVATAR_SIZE = {compact: 'xsmall' as const,balanced: 'small' as const,spacious: 'small' as const,};export default function ChatMessageListDensity() {return (<XDSVStack gap={4}>{DENSITIES.map(density => (<XDSVStack key={density} gap={2}><XDSText type="supporting" color="secondary">{density.charAt(0).toUpperCase() + density.slice(1)}</XDSText><XDSVStack xstyle={styles.section}><XDSChatMessageList density={density}><XDSChatMessage sender="user"><XDSChatMessageBubble>How does density work?</XDSChatMessageBubble></XDSChatMessage><XDSChatMessagesender="assistant"avatar={<XDSAvatar name="Agent" size={AVATAR_SIZE[density]} />}><XDSChatMessageBubble>Density controls spacing at every level — gap betweenmessages, padding inside bubbles, and gap between childelements.</XDSChatMessageBubble></XDSChatMessage></XDSChatMessageList></XDSVStack></XDSVStack>))}</XDSVStack>);}
tsx'use client';import {XDSChatMessageList,XDSChatMessage,XDSChatMessageBubble,XDSChatMessageMetadata,XDSChatSystemMessage,} from '@xds/core/Chat';import {XDSVStack, XDSHStack} from '@xds/core/Layout';import {XDSAvatar} from '@xds/core/Avatar';import {XDSMarkdown} from '@xds/core/Markdown';import {XDSCodeBlock} from '@xds/core/CodeBlock';import {XDSTimestamp} from '@xds/core/Timestamp';import {XDSToken} from '@xds/core/Token';import * as stylex from '@stylexjs/stylex';const styles = stylex.create({container: {maxWidth: 600,justifyContent: 'center',},});export default function ChatMessageListFullFeatured() {return (<XDSVStack xstyle={styles.container}><XDSChatMessageList><XDSChatSystemMessage variant="divider">Today</XDSChatSystemMessage><XDSChatMessage sender="user"><XDSHStack gap={2} wrap="wrap"><XDSToken label="useReducer.ts" /><XDSToken label="formState.ts" /></XDSHStack><XDSChatMessageBubblemetadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-03-15T14:30:00" format="time" />}status="read"/>}>Can you review these files?</XDSChatMessageBubble></XDSChatMessage><XDSChatMessagesender="assistant"avatar={<XDSAvatar name="Agent" size="small" />}><XDSChatMessageBubble group="first"><XDSMarkdown density="compact">{`Sure! Here's the key pattern from **useReducer.ts**:`}</XDSMarkdown></XDSChatMessageBubble><XDSChatMessageBubble variant="ghost" group="middle"><XDSCodeBlockcode={`const [state, dispatch] = useReducer((state, action) => ({...state,[action.field]: action.value,}),{ name: '', email: '' });`}language="tsx"/></XDSChatMessageBubble><XDSChatMessageBubblegroup="last"metadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-03-15T14:30:30" format="time" />}/>}><XDSMarkdown density="compact">{`The reducer is **pure and easy to test** — pass in state and action, assert on the output.`}</XDSMarkdown></XDSChatMessageBubble></XDSChatMessage><XDSChatSystemMessage>Agent shared a code snippet</XDSChatSystemMessage><XDSChatMessage sender="user"><XDSChatMessageBubblemetadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-03-15T14:31:00" format="time" />}status="delivered"/>}>That's clean, thanks!</XDSChatMessageBubble></XDSChatMessage></XDSChatMessageList></XDSVStack>);}
tsx'use client';import {XDSChatMessageList,XDSChatMessage,XDSChatMessageBubble,XDSChatMessageMetadata,XDSChatSystemMessage,} from '@xds/core/Chat';import {XDSVStack} from '@xds/core/Layout';import {XDSTimestamp} from '@xds/core/Timestamp';import * as stylex from '@stylexjs/stylex';const styles = stylex.create({container: {maxWidth: 600,height: '100%',},});export default function ChatMessageListShowcase() {return (<XDSVStack xstyle={styles.container}><XDSChatMessageList density="balanced"><XDSChatSystemMessage variant="divider">March 15, 2026</XDSChatSystemMessage><XDSChatMessage sender="user"><XDSChatMessageBubblemetadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-03-15T14:30:00" format="time" />}status="read"/>}>How should I structure a monorepo?</XDSChatMessageBubble></XDSChatMessage><XDSChatMessage sender="assistant"><XDSChatMessageBubblemetadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-03-15T14:30:15" format="time" />}/>}>Use workspaces with a shared packages directory. Keep each packagefocused on a single concern.</XDSChatMessageBubble></XDSChatMessage><XDSChatMessage sender="user"><XDSChatMessageBubblemetadata={<XDSChatMessageMetadatatimestamp={<XDSTimestamp value="2026-03-15T14:31:00" format="time" />}status="delivered"/>}>Should I use Yarn or pnpm for that?</XDSChatMessageBubble></XDSChatMessage></XDSChatMessageList></XDSVStack>);}