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

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"

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

Chat.BubbleContent

PropTypeDefault
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

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
aboveReactNodeContent 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
leadingReactNodeExtra controls at the start of the input row (attach button, voice recorder), bottom-aligned with the send button as the textarea grows.
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.Content

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

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

Chat.Group

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

Chat.Header

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

Chat.Item

PropTypeDefault
messageIdstring
scrollAnchorboolean

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

Chat.Marker

PropTypeDefault
variant"border" | "default" | "separator""default"
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

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

Chat.MarkerIcon

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

Chat.Message

PropTypeDefault
align"end" | "start""start"

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

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

Chat.Root

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"
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)

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

Chat.Time

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

Chat.Transcript

PropTypeDefault
spacerClassNamestring

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

Chat.UnreadSeparator

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

Chat.Viewport

PropTypeDefault
preserveScrollOnPrependboolean

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