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.Provider autoScroll>
	<Chat.Root>
		<Chat.Viewport aria-label="Messages">
			<Chat.Transcript>
				<Chat.Item messageId="day-1">
					<Chat.DateSeparator>Hier</Chat.DateSeparator>
				</Chat.Item>
				<Chat.Item messageId="m1">
					<Chat.Message align="start">
						<Chat.Avatar><Avatar.Root …/></Chat.Avatar>
						<Chat.Content>
							<Chat.Header>Camille Dubois</Chat.Header>
							<Chat.BubbleGroup>
								<Chat.Bubble variant="muted">
									<Chat.BubbleContent>
										<Chat.Text>Bonjour, je serai sur place 30 minutes avant.</Chat.Text>
										<Chat.Time dateTime="2026-06-11T18:42">18:42</Chat.Time>
									</Chat.BubbleContent>
								</Chat.Bubble>
							</Chat.BubbleGroup>
						</Chat.Content>
					</Chat.Message>
				</Chat.Item>
			</Chat.Transcript>
		</Chat.Viewport>
		<Chat.ScrollButton label="Voir les derniers messages" />
	</Chat.Root>
</Chat.Provider>
 
<Chat.Composer value={draft} onValueChange={setDraft} onSubmit={send} sendLabel="Envoyer" />

The scroller keeps a reader who is at the live edge pinned to new content, holds a turn at the reading line while a reply streams in, and preserves the position when older history is prepended — so an arriving message never yanks the view away from what is being read. useChatScroller exposes scrollToEnd, scrollToStart and scrollToMessage; useChatScrollerScrollable reports which edges still have content; useChatScrollerVisibility reports the visible rows and the current anchored turn.

Chat.MessageList and its Chat.Message* siblings are the earlier, self-scrolling anatomy, kept for the screens still on it. Its bubble is Chat.MessageBubble (the own/other variants).

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.Avatar

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Bubble

PropTypeDefault
align"end" | "start""start"
variant"default" | "destructive" | "ghost" | "muted" | "outline" | "secondary" | "tinted""default"
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.BubbleContent

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.
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-bubble-content.tsx.

Chat.BubbleGroup

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

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
aboveReactNode—Content above the input row — typically a Chat.ComposerAttachments tray of pending uploads.
allowEmptySubmitbooleanfalseLet an empty (or whitespace-only) draft submit. For composers that can send something other than text: the app sets it while an attachment is ready to go, so the send button stays live with nothing typed.
disabledbooleanfalseComposer unusable (e.g. archived conversation).
errorReactNode—
hintReactNode—
leadingReactNode—Extra controls at the start of the input row (attach button, voice recorder), bottom-aligned with the send button as the textarea grows.
maxLengthnumber—Soft 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.
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Content

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.ConversationItem

PropTypeDefault
titlestring | (string & Iterable<ReactNode>) | (string & Promise<AwaitedReactNode>) | (string & ReactElement<unknown, string | JSXElementConstructor<any>>) | (string & ReactPortal) | undefinedrequired
badgesReactNode—Subject/status badges, rendered after the title.
descriptionReactNode—Secondary line: last-message preview or "no messages yet" copy.
leadingReactNode—Leading visual (typically an avatar), rendered before the text column.
timestampReactNode—
unreadCountnumber0Messages the reader has not seen; > 0 bolds the row and shows a count.
unreadLabelstring—Accessible 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.
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.
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

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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.
descriptionReactNode—Warning 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.Footer

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Group

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Header

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Item

PropTypeDefault
messageIdstring—
scrollAnchorboolean—
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Marker

PropTypeDefault
variant"border" | "default" | "separator""default"
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.
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-marker.tsx.

Chat.MarkerContent

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

Plus every <span> prop. Source: chat/components/chat-marker-content.tsx.

Chat.MarkerIcon

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

Plus every <span> prop. Source: chat/components/chat-marker-icon.tsx.

Chat.Message

PropTypeDefault
align"end" | "start""start"
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.MessageBubble

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.
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.
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-bubble.tsx.

Chat.MessageGroup

PropTypeDefault
align"end" | "start"required
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.
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".
headerReactNode—Pinned above the messages inside the scroll area (e.g. "load older").
jumpToLatestLabelReactNode—Label 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.
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.MessageSender

PropTypeDefault
nameReactNoderequired
avatarReactNode—
badgeReactNode—
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.MessageText

PropTypeDefault
childrenstringrequired
onLinkClick((url: string) => void)—
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.MessageTime

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Provider

PropTypeDefault
autoScrollbooleanfalse
childrenReactNode—
defaultScrollPositionMessageScrollerDefaultScrollPosition"end"
scrollEdgeThresholdnumber—
scrollMarginnumber—
scrollPreviousItemPeeknumber—

Plus the DOM props of the element it renders. Source: chat/components/message-scroller-provider.tsx.

Chat.Reactions

PropTypeDefault
align"end" | "start""end"
side"bottom" | "top""bottom"
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Root

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.ScrollButton

PropTypeDefault
labelstringrequiredAccessible name for the icon-only affordance, e.g. "Voir les derniers messages".
behaviorScrollBehavior—
directionMessageScrollerButtonDirection"end"
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.
renderComponentRenderFn<HTMLProps, MessageScrollerButtonRenderState> | 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 the DOM props of the element it renders. Source: chat/components/chat-scroll-button.tsx.

Chat.Text

PropTypeDefault
childrenstringrequired
onLinkClick((url: string) => void)—
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Time

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Transcript

PropTypeDefault
spacerClassNamestring—
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.UnreadSeparator

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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

Chat.Viewport

PropTypeDefault
preserveScrollOnPrependboolean—
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

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