Component reference
Chat
Send messages in a local team conversation.
Preview
Live previewOptional six-digit hex. Clear to restore the preset. Check contrast with your brand color.
Changes apply to this preview only.
Enter sends · Shift + Enter adds a line.
- Yesterday
- Today
Width adapts to the available space
"use client";
import { useState, type Dispatch, type SetStateAction } from "react";
import {
Button,
Chat,
type PimoluChatMessage,
AvatarGroup,
Avatar,
Badge
} from "@pimolu/react";
const currentUser = {
id: "maya",
name: "Maya Chen",
};
const lucas = {
id: "lucas",
name: "Lucas Moreau",
};
const sofia = {
id: "sofia",
name: "Sofia Rossi",
};
const noah = {
id: "noah",
name: "Noah Dubois",
};
const initialMessages: PimoluChatMessage[] = [
{
id: "m1",
author: lucas,
body: (
"I published the semantic aliases for the next preview. The old " +
"token names still resolve, so nothing breaks yet."
),
time: "17:42",
date: "2026-10-19",
dateLabel: "Yesterday",
},
{
id: "m2",
author: currentUser,
body: (
"Perfect. The dark theme resolves the surface and border aliases " +
"without a flash on first paint."
),
time: "17:45",
date: "2026-10-19",
status: "sent",
},
{
id: "m3",
author: sofia,
body: "Here is the contrast audit. Two amber badges still fail AA against the glass preset.",
time: "09:12",
date: "2026-10-20",
dateLabel: "Today",
attachments: [
{
id: "attachment-audit",
name: "contrast-audit.pdf",
href: "#contrast-audit",
size: 184320,
},
],
},
{
id: "m4",
author: lucas,
body: (
"Thanks Sofia. The two amber badges are the only blockers, so I " +
"will fix them before the preview build."
),
time: "09:15",
date: "2026-10-20",
},
{
id: "m5",
author: currentUser,
body: "I'll take the chart pass this morning and report back with the updated screenshots.",
time: "09:17",
date: "2026-10-20",
status: "sending",
},
];
function nowTime() {
return new Intl.DateTimeFormat(undefined, {
hour: "2-digit",
minute: "2-digit",
}).format(new Date());
}
function todayIso() {
const date = new Date();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${date.getFullYear()}-${month}-${day}`;
}
export function DemoExample() {
const [messages, setMessages] = useState<PimoluChatMessage[]>(initialMessages);
const [typing, setTyping] = useState(true);
const [notice, setNotice] = useState("Enter sends · Shift + Enter adds a line.");
function appendMessage(setter: Dispatch<SetStateAction<PimoluChatMessage[]>>, body: string) {
setter((current) => [
...current,
{
id: `local-${Date.now()}`,
author: currentUser,
body,
time: nowTime(),
date: todayIso(),
status: "sent",
},
]);
setNotice("Message appended locally; connect onSend to your API.");
}
return (
<div className="grid gap-5 min-w-0">
<div className="flex flex-wrap items-center gap-3 min-w-0 justify-between">
<p className="text-sm text-muted leading-relaxed">{notice}</p>
<Button
size="sm"
appearance="outline"
onClick={() => setTyping((value) => !value)}
>
{
typing
? "Hide typing indicator"
: "Show typing indicator"
}
</Button>
</div>
<Chat
messages={messages}
currentUserId={currentUser.id}
onSend={(body) => appendMessage(
setMessages,
body
)}
typingUsers={typing
? [
sofia.name,
lucas.name
]
: []}
onAttach={() => setNotice("Attachment picker requested (demo). Wire onAttach to your file flow.")}
header={<>
<div className="flex flex-wrap items-center gap-3 min-w-0">
<AvatarGroup max={3} size="sm" aria-label="Conversation members">
<Avatar name={currentUser.name} />
<Avatar name={lucas.name} />
<Avatar name={sofia.name} />
<Avatar name={noah.name} />
</AvatarGroup>
<div>
<strong>Design system</strong>
<div className="text-sm text-muted leading-relaxed">#general · 4 members</div>
</div>
</div>
<Badge variant="success" dot>Online</Badge>
</>}
/>
</div>
);
}More examples
Different configurations and compositions using the real Pimolu API. Each preview runs independently and follows the site appearance.
New conversation
A new conversation composition using Chat.
Loading history
A loading history composition using Chat.
Read-only conversation
The disabled prop turns the composer off while the archive stays readable. Status labels remain available to assistive technology.
Usage
Keep transport, authentication and delivery status in your own messaging service.
API reference
Generated from the public TypeScript declarations. Native HTML attributes are inherited where the component extends them; this reference includes Pimolu props and selected common native attributes.
PimoluChatAuthor
Type source| Name | Type | Default | Description |
|---|---|---|---|
idRequired | string | — | Public Pimolu prop; see the source declaration for details. |
nameRequired | string | — | Public Pimolu prop; see the source declaration for details. |
avatar | string | undefined | — | Public Pimolu prop; see the source declaration for details. |
idRequiredstringNo default extracted.
Public Pimolu prop; see the source declaration for details.
nameRequiredstringNo default extracted.
Public Pimolu prop; see the source declaration for details.
avatarstring | undefinedNo default extracted.
Public Pimolu prop; see the source declaration for details.
PimoluChatAttachment
Type source| Name | Type | Default | Description |
|---|---|---|---|
idRequired | string | — | Public Pimolu prop; see the source declaration for details. |
nameRequired | string | — | Public Pimolu prop; see the source declaration for details. |
href | string | undefined | — | When provided, the attachment is rendered as a link. |
size | number | undefined | — | Size in bytes. |
idRequiredstringNo default extracted.
Public Pimolu prop; see the source declaration for details.
nameRequiredstringNo default extracted.
Public Pimolu prop; see the source declaration for details.
hrefstring | undefinedNo default extracted.
When provided, the attachment is rendered as a link.
sizenumber | undefinedNo default extracted.
Size in bytes.
PimoluChatMessage
Type source| Name | Type | Default | Description |
|---|---|---|---|
idRequired | string | — | Stable message id. |
authorRequired | PimoluChatAuthor | — | Message author. |
bodyRequired | ReactNode | — | Message content. Newlines are preserved. |
timeRequired | string | — | Display time, for example "09:42". |
date | string | undefined | — | Optional ISO date used to group messages under date separators. Messages without a date never open a separator. |
dateLabel | string | undefined | — | Explicit separator label for this message's date. |
own | boolean | undefined | comparing the author id with `currentUserId` | Marks an outgoing message. Defaults to comparing the author id with `currentUserId`. |
status | PimoluChatMessageStatus | undefined | — | Delivery state, shown next to outgoing messages. |
attachments | readonly PimoluChatAttachment[] | undefined | — | Attached files. |
idRequiredstringNo default extracted.
Stable message id.
authorRequiredPimoluChatAuthorNo default extracted.
Message author.
bodyRequiredReactNodeNo default extracted.
Message content. Newlines are preserved.
timeRequiredstringNo default extracted.
Display time, for example "09:42".
datestring | undefinedNo default extracted.
Optional ISO date used to group messages under date separators. Messages without a date never open a separator.
dateLabelstring | undefinedNo default extracted.
Explicit separator label for this message's date.
ownboolean | undefinedDefault: comparing the author id with `currentUserId`
Marks an outgoing message. Defaults to comparing the author id with `currentUserId`.
statusPimoluChatMessageStatus | undefinedNo default extracted.
Delivery state, shown next to outgoing messages.
attachmentsreadonly PimoluChatAttachment[] | undefinedNo default extracted.
Attached files.
PimoluChatLabels
Type source| Name | Type | Default | Description |
|---|---|---|---|
conversationRequired | string | — | Accessible name of the conversation region. |
messagesRequired | string | — | Accessible name of the message log. |
messageRequired | string | — | Accessible label of the composer. |
placeholderRequired | string | — | Default composer placeholder. |
sendRequired | string | — | Accessible name of the send button. |
attachRequired | string | — | Accessible name of the attachment button. |
emptyRequired | string | — | Empty-state title. |
emptyDescriptionRequired | string | — | Empty-state description. |
loadingRequired | string | false | Announced while the conversation loads. |
typingRequired | (names: string) => string | — | Typing indicator; receives the joined author names. |
youRequired | string | — | Author label for outgoing messages. |
sendingRequired | string | — | Accessible status label. |
sentRequired | string | — | Accessible status label. |
failedRequired | string | — | Visible status label. |
attachmentRequired | (name: string) => string | — | Accessible name of an attachment. |
conversationRequiredstringNo default extracted.
Accessible name of the conversation region.
messagesRequiredstringNo default extracted.
Accessible name of the message log.
messageRequiredstringNo default extracted.
Accessible label of the composer.
placeholderRequiredstringNo default extracted.
Default composer placeholder.
sendRequiredstringNo default extracted.
Accessible name of the send button.
attachRequiredstringNo default extracted.
Accessible name of the attachment button.
emptyRequiredstringNo default extracted.
Empty-state title.
emptyDescriptionRequiredstringNo default extracted.
Empty-state description.
loadingRequiredstringDefault: false
Announced while the conversation loads.
typingRequired(names: string) => stringNo default extracted.
Typing indicator; receives the joined author names.
youRequiredstringNo default extracted.
Author label for outgoing messages.
sendingRequiredstringNo default extracted.
Accessible status label.
sentRequiredstringNo default extracted.
Accessible status label.
failedRequiredstringNo default extracted.
Visible status label.
attachmentRequired(name: string) => stringNo default extracted.
Accessible name of an attachment.
PimoluChatProps
Type source| Name | Type | Default | Description |
|---|---|---|---|
messagesRequired | readonly PimoluChatMessage[] | — | Conversation messages, oldest first. Never mutated. |
currentUserIdRequired | string | — | Id used to identify outgoing messages. |
onSendRequired | (body: string) => void | — | Called with the trimmed composer value. |
placeholder | string | undefined | — | Overrides the default composer placeholder. |
disabled | boolean | undefined | false | Disables the whole composer. |
loading | boolean | undefined | false | Replaces the message area with a loading state. |
header | ReactNode | — | Custom header content, for example a channel name and presence. |
emptyState | ReactNode | — | Replaces the default empty state. |
typingUsers | readonly string[] | undefined | [] | Names shown in the typing indicator. Empty hides it. |
onAttach | (() => void) | undefined | — | Shows an attachment button when provided. |
sendOnEnter | boolean | undefined | true | When true (default), Enter sends and Shift + Enter inserts a newline. When false, only the send button submits. |
maxLength | number | undefined | — | Native textarea maxlength. |
labels | Partial<PimoluChatLabels> | undefined | — | Overrides the built-in accessible labels. |
onChange | ChangeEventHandler<HTMLDivElement, Element> | undefined | — | Standard React/HTML attribute. |
defaultValue | string | number | readonly string[] | undefined | — | Standard React/HTML attribute. |
className | string | undefined | — | Standard React/HTML attribute. |
style | CSSProperties | undefined | — | Standard React/HTML attribute. |
aria-label | string | undefined | — | Defines a string value that labels the current element. |
onClick | MouseEventHandler<HTMLDivElement> | undefined | — | Standard React/HTML attribute. |
messagesRequiredreadonly PimoluChatMessage[]No default extracted.
Conversation messages, oldest first. Never mutated.
currentUserIdRequiredstringNo default extracted.
Id used to identify outgoing messages.
onSendRequired(body: string) => voidNo default extracted.
Called with the trimmed composer value.
placeholderstring | undefinedNo default extracted.
Overrides the default composer placeholder.
disabledboolean | undefinedDefault: false
Disables the whole composer.
loadingboolean | undefinedDefault: false
Replaces the message area with a loading state.
headerReactNodeNo default extracted.
Custom header content, for example a channel name and presence.
emptyStateReactNodeNo default extracted.
Replaces the default empty state.
typingUsersreadonly string[] | undefinedDefault: []
Names shown in the typing indicator. Empty hides it.
onAttach(() => void) | undefinedNo default extracted.
Shows an attachment button when provided.
sendOnEnterboolean | undefinedDefault: true
When true (default), Enter sends and Shift + Enter inserts a newline. When false, only the send button submits.
maxLengthnumber | undefinedNo default extracted.
Native textarea maxlength.
labelsPartial<PimoluChatLabels> | undefinedNo default extracted.
Overrides the built-in accessible labels.
onChangeChangeEventHandler<HTMLDivElement, Element> | undefinedNo default extracted.
Standard React/HTML attribute.
defaultValuestring | number | readonly string[] | undefinedNo default extracted.
Standard React/HTML attribute.
classNamestring | undefinedNo default extracted.
Standard React/HTML attribute.
styleCSSProperties | undefinedNo default extracted.
Standard React/HTML attribute.
aria-labelstring | undefinedNo default extracted.
Defines a string value that labels the current element.
onClickMouseEventHandler<HTMLDivElement> | undefinedNo default extracted.
Standard React/HTML attribute.
Accessibility
- Label the composer and announce new messages without repeatedly reading the whole history.