Data display
Chat
Conversations: messages in bubbles or plain, runs from the same author, day separators, a New messages line, delivery states, reactions, thread links, hover actions and a typing indicator.
import { MessageList, ChatMessage, ReactionBar, TypingIndicator } from '@alea/ui';
Examples
Conversation
A MessageList with day separators, runs from the same author, a New messages line, delivery states, reactions and a thread link. Send a message to see it follow the newest one.
Message, reactions and typing on their own
ChatMessage in the bubble and the plain look, a failed message, ReactionBar with the picker, and TypingIndicator for one, two or many people.
Tokens
The design tokens this component reads, with their value in the current theme. Override any of them to restyle it.
Color
--alea-border…--alea-focus…--alea-status-danger…--alea-text-muted…
Space
--alea-space-1…--alea-space-2…--alea-space-3…--alea-space-4…--alea-space-5…
Radius
--alea-radius-md…
Type
--alea-font-family-body…--alea-font-size-xs…--alea-font-weight-semibold…
Props
MessageList props
A conversation: day separators (Today, Yesterday, a date), runs from the same author, a New messages line, a typing indicator, and scrolling that follows new messages. A log region, so screen readers hear new messages as they arrive.
| Prop | Type | Default | Description |
|---|---|---|---|
messagesrequired | ChatItem[] | - | The messages, oldest first. |
aria-label | string | - | Name for screen readers. Default "Messages". |
className | string | - | Extra class names. |
firstUnreadId | string | - | Id of the first unread message: a "New messages" line shows above it. |
groupMinutes | number | 5 | Messages from the same author within this many minutes form a run (no repeated avatar and name). Default 5. |
height | string | number | 420 | Height of the scrolling list; it starts at the newest message and follows new ones while you are at the bottom. |
onOpenThread | ((messageId: string) => void) | - | Called when a thread link is pressed. |
onReact | ((messageId: string, emoji: string) => void) | - | Called when a reaction is toggled on a message. |
renderActions | ((m: ChatItem) => ReactNode) | - | Extra actions per message, shown on hover and focus. |
typing | string[] | - | Who is typing, shown at the end. |
variant | "bubble" | "plain" | 'bubble' | bubble (default) or plain. |
ChatMessage props
One message in a conversation: avatar and name (hidden inside a run), a bubble or plain text, time, delivery state, edited, reactions, a thread link and hover actions.
| Prop | Type | Default | Description |
|---|---|---|---|
authorrequired | string | - | Who wrote it. |
childrenrequired | ReactNode | - | The message: text, or anything (images, files, cards). |
actions | ReactNode | - | Actions that show on hover and focus: reply, react, more (small IconButtons). |
avatarSrc | string | - | Their photo. |
className | string | - | Extra class names. |
isEdited | boolean | - | Marks it as edited. |
isGrouped | boolean | - | Part of a run from the same author: hides the avatar and the name. |
isOwn | boolean | - | Written by the current person: on the end side, in the accent bubble. |
onOpenThread | (() => void) | - | Opens the thread. |
onReact | ((emoji: string) => void) | - | Called when a reaction is toggled; with it, the reactions get an add button. |
reactions | Reaction[] | - | Reactions under the bubble. |
replyCount | number | - | Number of replies in its thread, shown as a link. |
status | "sending" | "sent" | "delivered" | "read" | "failed" | - | Delivery state for own messages: sending, sent, delivered, read or failed. |
time | Date | - | When it was sent: shown as a short time, with the full date on hover. |
variant | "bubble" | "plain" | 'bubble' | bubble (default) or plain (no bubble, full width, like a channel). |
ReactionBar props
Emoji reactions under a message or a comment: each is a toggle with its count, and the add button opens an EmojiPicker.
| Prop | Type | Default | Description |
|---|---|---|---|
onTogglerequired | (emoji: string) => void | - | Called when someone toggles a reaction (an existing one, or a new one from the picker). |
reactionsrequired | Reaction[] | - | The reactions so far. |
allowAdd | boolean | true | Shows the add button that opens the emoji picker. Default true. |
className | string | - | Extra class names. |
size | "sm" | "md" | 'md' | sm (24 px) or md (28 px, default). |
TypingIndicator props
"Ana is typing" with three bouncing dots. Announced politely once when it appears; the dots are decoration and stop with reduced motion.
| Prop | Type | Default | Description |
|---|---|---|---|
namesrequired | string[] | - | Who is typing. Empty hides it. |
className | string | - | Extra class names. |
Specs
Generated from the source: the variants in the types, the states and tokens in the CSS, and the motion patterns that apply. The grids render the real component in both themes; the numbers are measured from the page.
MessageList
- Variants
variantbubbleplain- States
- Keyboard focus
- Dark and light
- Every color comes from theme tokens, so it follows data-theme (and presets like Graphite) on the page or any wrapper. Contrast is checked in both themes.
- Styling hooks
.alea-msglist.alea-msglist-day.alea-msglist-new
ChatMessage
- Variants
statussendingsentdeliveredreadfailedvariantbubbleplain- States
- Hover (CSS)Focus inside
- Dark and light
- Every color comes from theme tokens, so it follows data-theme (and presets like Graphite) on the page or any wrapper. Contrast is checked in both themes.
- Styling hooks
.alea-msg.alea-msg-avatar.alea-msg-main.alea-msg-head.alea-msg-author.alea-msg-time.alea-msg-body.alea-msg-bubble.alea-msg-actions.alea-msg-meta.alea-msg-status.alea-msg-thread- Data attributes
data-groupeddata-variant=bubbledata-variant=plaindata-status=readdata-status=faileddata-status=sendingdata-own
ReactionBar
- Variants
sizesmmd- States
- Hover (CSS)Pressed (CSS)
- Dark and light
- Every color comes from theme tokens, so it follows data-theme (and presets like Graphite) on the page or any wrapper. Contrast is checked in both themes.
- Styling hooks
.alea-reactions.alea-reaction.alea-reaction-count.alea-reaction-add.alea-emoji-popover- Data attributes
data-size=sm
TypingIndicator
- Dark and light
- Every color comes from theme tokens, so it follows data-theme (and presets like Graphite) on the page or any wrapper. Contrast is checked in both themes.
- Styling hooks
.alea-typing.alea-typing-dots.alea-typing-text
Accessibility
- MessageList is a log region: screen readers announce new messages as they arrive, without moving focus.
- The list takes focus as one stop, so keyboard users can scroll it; actions inside each message are reachable with Tab.
- Each message is an article named by its author and time; delivery states are words, not only icons.
- Reactions are toggle buttons that say the emoji, the count and whether you reacted.
- Hover actions also show on keyboard focus and stay visible on touch screens.
- The typing indicator is a polite status; its dots are decoration and stop with reduced motion.
Keyboard
| Key | Does |
|---|---|
| Tab | Moves into the list, then through each message's actions and reactions |
| Arrow keys | Scroll the list when it has focus |
| Space or Enter | Toggles a reaction or opens the picker |
Do and don't
Do
- Group runs from the same author so long conversations stay readable.
- Show the New messages line when people come back to a conversation.
- Keep hover actions to three or four: reply, react, more.
Don't
- Don't rely on the check icons alone for delivery: the status words are there for that.
- Don't auto-scroll when people have scrolled up to read; the list only follows new messages while they are at the bottom.
- Don't put long forms inside a message: link to them.
In Figma
ChatMessage with Own, Grouped, Variant (Bubble, Plain) and Status; ReactionBar and TypingIndicator as separate components.