Chat

Message threads, bubbles and a composer.

CDCamille DuboisVerified freelancer
Hi, I will share the first drafts before our call tomorrow.
Is there a shared folder where I can drop the files?
Yes, the project drive is already shared with you.
<Chat.MessageList>
	<Chat.DateSeparator>Hier</Chat.DateSeparator>
	<Chat.MessageGroup align="start">
		<Chat.MessageSender avatar={<Avatar.Root …/>} name="Camille Dubois" />
		<Chat.Message variant="other">
			Bonjour, je serai sur place 30 minutes avant le match.
			<Chat.MessageTime dateTime="2026-06-11T18:42">18:42</Chat.MessageTime>
		</Chat.Message>
	</Chat.MessageGroup>
</Chat.MessageList>
 
<Chat.Composer value={draft} onValueChange={setDraft} onSubmit={send} sendLabel="Envoyer" />

The list keeps itself pinned to the newest message while the user is at the bottom, and stops following as soon as they scroll up — so an arriving message never yanks the view away from what they are reading.

Group consecutive messages from one author into a single Chat.MessageGroup; the sender block is rendered once per group, not once per message.

SU
SupportSupport
Our team replies to you directly here.
09:102
RC
Website launch — April 12Milestone
Archived conversation
12/04

Chat.ConversationItem is the list-of-threads row, with unreadCount capped at "99+".

API

Chat.Composer

PropTypeDefault
onSubmit() => voidrequiredFired on send button click, on (Cmd|Ctrl)+Enter, and — when submitOnEnter is set — on plain Enter.
onValueChange(value: string) => voidrequired
sendLabelstringrequired
valuestringrequired
disabledbooleanfalseComposer unusable (e.g. archived conversation).
errorReactNode
hintReactNode
maxLengthnumberSoft character limit: shows a live counter and disables send once exceeded. The textarea itself is not capped so the writer can trim an over-long draft instead of having input silently dropped.
placeholderstring
sendingbooleanfalseA post is in flight: button shows a spinner, submit is suppressed. The textarea stays enabled so the writer keeps focus and can draft the next message.
submitOnEnterbooleanfalseSend on a plain Enter (Shift+Enter still inserts a newline), matching the mobile/messenger convention. Cmd/Ctrl+Enter always sends regardless.

Plus every <form> prop. Source: chat/components/chat-composer.tsx.

Chat.ConversationItem

PropTypeDefault
titlestring | (string & Iterable<ReactNode>) | (string & Promise<AwaitedReactNode>) | (string & ReactElement<unknown, string | JSXElementConstructor<any>>) | (string & ReactPortal) | undefinedrequired
badgesReactNodeSubject/status badges, rendered after the title.
descriptionReactNodeSecondary line: last-message preview or "no messages yet" copy.
leadingReactNodeLeading visual (typically an avatar), rendered before the text column.
timestampReactNode
unreadCountnumber0Messages the reader has not seen; > 0 bolds the row and shows a count.
unreadLabelstringAccessible label for the unread badge (the visible glyph is a bare count, capped at "99+"). Spell out the true total here, e.g. "3 unread messages", so screen readers don't announce a contextless number.
renderComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render.

Plus every <div> prop. Source: chat/components/chat-conversation-item.tsx.

Chat.DateSeparator

Plus every <div> prop. Source: chat/components/chat-date-separator.tsx.

Chat.ExternalLinkDialog

PropTypeDefault
cancelLabelReactNoderequired
confirmLabelReactNoderequired
onClose() => voidrequired
titleReactNoderequired
urlstring | nullrequiredThe pending external URL; null keeps the dialog closed.
descriptionReactNodeWarning copy shown above the URL, e.g. "Do you trust this link?".

Plus the DOM props of the element it renders. Source: chat/components/chat-external-link-dialog.tsx.

Chat.Message

PropTypeDefault
variant"other" | "own"requiredWhich side of the conversation the bubble belongs to. own is the signed-in user. This only tints and notches the bubble — the enclosing ChatMessageGroup is what aligns it left or right.
renderComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render.

Plus every <div> prop. Source: chat/components/chat-message.tsx.

Chat.MessageGroup

PropTypeDefault
align"end" | "start"required
renderComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render.

Plus every <div> prop. Source: chat/components/chat-message-group.tsx.

Chat.MessageList

PropTypeDefault
followThresholdnumberFOLLOW_THRESHOLDHow close to the bottom (px) still counts as "following".
headerReactNodePinned above the messages inside the scroll area (e.g. "load older").
jumpToLatestLabelReactNodeLabel for the floating "jump to latest ↓" button shown while the reader is away from the bottom and new content may arrive. Omit to disable the affordance.
onFollowChange((following: boolean) => void)Fired when the reader starts or stops following the bottom of the thread. Lets consumers react to the internal follow state (analytics, unread markers, …) beyond the built-in jump-to-latest affordance.

Plus every <div> prop. Source: chat/components/chat-message-list.tsx.

Chat.MessageSender

PropTypeDefault
nameReactNoderequired
avatarReactNode
badgeReactNode

Plus every <div> prop. Source: chat/components/chat-message-sender.tsx.

Chat.MessageText

PropTypeDefault
childrenstringrequired
onLinkClick((url: string) => void)

Plus every <span> prop. Source: chat/components/chat-message-text.tsx.

Chat.MessageTime

Plus every <time> prop. Source: chat/components/chat-message-time.tsx.

Chat.UnreadSeparator

Plus every <div> prop. Source: chat/components/chat-unread-separator.tsx.