AI chat
Chat message
One turn in a conversation.
Import
import { ChatMessage } from '@the_viveksingh/vivek-ui'A conversation turn
role drives the alignment and the accessible role, so user, assistant and system turns are distinguishable without reading the colours.
<ChatMessage role="system" content="You are chatting with the support assistant." />
<ChatMessage
role="user"
name="You"
avatar={<Avatar name="Vivek Kumar Singh" size="sm" />}
content="How do I override a component's padding?"
timestamp={sentAt}
status="sent"
/>
<ChatMessage
role="assistant"
name="Assistant"
avatar={<Avatar fallback="AI" size="sm" />}
content="Pass a className. Library selectors sit inside :where()."
timestamp={repliedAt}
/>Flat variant
No bubble. Better for long answers, where a bubble becomes a wall of colour.
<ChatMessage variant="flat" role="user" name="You" content="Summarise this thread." />
<ChatMessage variant="flat" role="assistant" name="Assistant" content="Three open questions remain." />Delivery status
sending, sent and error each get a visually hidden label, so a failed send is announced rather than only tinted. Use actions for the retry affordance.
<ChatMessage role="user" content="Sending this one now" status="sending" />
<ChatMessage role="user" content="This one landed" status="sent" />
<ChatMessage
role="user"
content="This one failed to send"
status="error"
actions={<Button size="sm" variant="ghost" onClick={retry}>Retry</Button>}
/>Props
Generated from the package's own type declarations, so this table cannot drift from the code.
| Prop | Type | Default | Description |
|---|---|---|---|
role | ChatMessageRole | — | Who is speaking. Defaults to `assistant`. |
content | ReactNode | — | The turn's body. A `ReactNode`, never a string that gets parsed — React escapes whatever goes in here, which is the whole reason this family needs no sanitiser. Falls back to `children` when omitted. |
avatar | ReactNode | — | Leading slot, typically an `<Avatar />`. Purely decorative for a11y purposes. |
name | string | — | Display name of the speaker. Defaults to a label derived from `role`. |
timestamp | Date | string | number | — | When the turn happened. A `Date` or epoch number is formatted for the current locale; a **string is rendered verbatim**, so `timestamp="2 min ago"` works and SSR output stays byte-identical when you pre-format. |
status | ChatMessageStatus | — | Delivery state. Defaults to `sent`, which renders no status text. |
actions | ReactNode | — | Trailing controls (copy, retry, thumbs up…). Rendered after the content. |
variant | ChatMessageVariant | — | `bubble` for a chat balloon, `flat` for a document-style transcript. |
formatTimestamp | (date: Date) => string | — | Override the default `Date` formatting. Also the escape hatch for strict SSR. |
statusLabels | Partial<Record<ChatMessageStatus, string>> | — | Localise the status text (also used in the accessible name). |
Every remaining prop is spread onto the root element, so all standard HTML and ARIA attributes work. className and style are merged with the library's own, never replaced, and the ref forwards to the root DOM node.
Rendering
ChatMessage carries no 'use client' directive and renders directly in a React Server Component. No client JavaScript is shipped for it.