<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).
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
| Prop | Type | Default | |
|---|---|---|---|
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
| Prop | Type | Default | |
|---|---|---|---|
render | ComponentRenderFn<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
| Prop | Type | Default | |
|---|---|---|---|
onSubmit | () => void | required | Fired on send button click, on (Cmd|Ctrl)+Enter, and — when
submitOnEnter is set — on plain Enter. |
onValueChange | (value: string) => void | required | |
sendLabel | string | required | |
value | string | required | |
above | ReactNode | — | Content above the input row — typically a Chat.ComposerAttachments
tray of pending uploads. |
allowEmptySubmit | boolean | false | Let 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. |
disabled | boolean | false | Composer unusable (e.g. archived conversation). |
error | ReactNode | — | |
hint | ReactNode | — | |
leading | ReactNode | — | Extra controls at the start of the input row (attach button, voice recorder), bottom-aligned with the send button as the textarea grows. |
maxLength | number | — | 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. |
placeholder | string | — | |
sending | boolean | false | A 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. |
submitOnEnter | boolean | false | Send 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
| Prop | Type | Default | |
|---|---|---|---|
title | string | (string & Iterable<ReactNode>) | (string & Promise<AwaitedReactNode>) | (string & ReactElement<unknown, string | JSXElementConstructor<any>>) | (string & ReactPortal) | undefined | required | |
badges | ReactNode | — | Subject/status badges, rendered after the title. |
description | ReactNode | — | Secondary line: last-message preview or "no messages yet" copy. |
leading | ReactNode | — | Leading visual (typically an avatar), rendered before the text column. |
timestamp | ReactNode | — | |
unreadCount | number | 0 | Messages the reader has not seen; > 0 bolds the row and shows a count. |
unreadLabel | string | — | 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. |
render | ComponentRenderFn<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
| Prop | Type | Default | |
|---|---|---|---|
cancelLabel | ReactNode | required | |
confirmLabel | ReactNode | required | |
onClose | () => void | required | |
title | ReactNode | required | |
url | string | null | required | The pending external URL; null keeps the dialog closed. |
description | ReactNode | — | 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
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
| Prop | Type | Default | |
|---|---|---|---|
messageId | string | — | |
scrollAnchor | boolean | — |
Plus the DOM props of the element it renders. Source: chat/components/chat-item.tsx.
Chat.Marker
| Prop | Type | Default | |
|---|---|---|---|
variant | "border" | "default" | "separator" | "default" | |
render | ComponentRenderFn<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
| Prop | Type | Default | |
|---|---|---|---|
align | "end" | "start" | "start" |
Plus every <div> prop. Source: chat/components/chat-message.tsx.
Chat.MessageBubble
| Prop | Type | Default | |
|---|---|---|---|
variant | "other" | "own" | required | Which 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. |
render | ComponentRenderFn<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
| Prop | Type | Default | |
|---|---|---|---|
align | "end" | "start" | required | |
render | ComponentRenderFn<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
| Prop | Type | Default | |
|---|---|---|---|
followThreshold | number | FOLLOW_THRESHOLD | How close to the bottom (px) still counts as "following". |
header | ReactNode | — | Pinned above the messages inside the scroll area (e.g. "load older"). |
jumpToLatestLabel | ReactNode | — | 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. |
Plus every <div> prop. Source: chat/components/chat-message-list.tsx.
Chat.MessageSender
| Prop | Type | Default | |
|---|---|---|---|
name | ReactNode | required | |
avatar | ReactNode | — | |
badge | ReactNode | — |
Plus every <div> prop. Source: chat/components/chat-message-sender.tsx.
Chat.MessageText
| Prop | Type | Default | |
|---|---|---|---|
children | string | required | |
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
| Prop | Type | Default | |
|---|---|---|---|
autoScroll | boolean | false | |
children | ReactNode | — | |
defaultScrollPosition | MessageScrollerDefaultScrollPosition | "end" | |
scrollEdgeThreshold | number | — | |
scrollMargin | number | — | |
scrollPreviousItemPeek | number | — |
Plus the DOM props of the element it renders. Source: chat/components/message-scroller-provider.tsx.
Chat.Reactions
| Prop | Type | Default | |
|---|---|---|---|
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
| Prop | Type | Default | |
|---|---|---|---|
label | string | required | Accessible name for the icon-only affordance, e.g. "Voir les derniers messages". |
behavior | ScrollBehavior | — | |
direction | MessageScrollerButtonDirection | "end" | |
render | ComponentRenderFn<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
| Prop | Type | Default | |
|---|---|---|---|
children | string | required | |
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
| Prop | Type | Default | |
|---|---|---|---|
spacerClassName | string | — |
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
| Prop | Type | Default | |
|---|---|---|---|
preserveScrollOnPrepend | boolean | — |
Plus the DOM props of the element it renders. Source: chat/components/chat-viewport.tsx.