Chat MessagePro
Complete messaging component system for building modern chat interfaces. Supports multiple message types (text, icon, image, file), sender-based layouts, inline reply previews, fully customizable emoji reactions, and smooth spring-animated reaction triggers.
Chat Message Playground

Can you provide me with more details on the project?
06:04 AM
Installation
You can add the chat message component to your project manually:
Install the following dependencies:
npm install class-variance-authority clsx tailwind-merge @phosphor-icons/react @react-aria/interactionsCopy and paste the following code into your project.
Update the import paths to match your project setup.
Update the import aliases (e.g. @/components, @/utils) in the copied files to match your project's path configuration.
Basic Usage
import { ChatMessage } from '@versaui/ui/components/ChatMessage';
// Basic text message from another user
<ChatMessage
sender="other"
senderName="Alice"
message="Hey, how's the project going?"
timestamp="10:30 AM"
/>
// User's own message
<ChatMessage
sender="user"
message="It's going great, thanks!"
timestamp="10:31 AM"
/>Variants
Sender
Messages align and style differently based on sender:
// Other user — left aligned, secondary color, with avatar
<ChatMessage sender="other" senderName="Bob" />
// Current user — right aligned, neutral color, no avatar
<ChatMessage sender="user" />Message Types
Four content types are supported:
// Text only
<ChatMessage messageType="text" message="Hello!" />
// Icon message (e.g. missed call)
<ChatMessage messageType="icon" iconLabel="Missed call" />
// Image message
<ChatMessage messageType="image" imageUrl="/photo.jpg" message="Check this out" />
// File attachment
<ChatMessage messageType="file" fileName="report.pdf" fileSize="412 KB" />State
Hover state adds elevation shadow and reveals the react button:
<ChatMessage state="default" />
<ChatMessage state="hovered" />With Reply
Show an inline reply preview inside the bubble:
<ChatMessage
reply
replyProps={{ type: 'text', senderName: 'Alice', content: 'Original message' }}
message="Yes, I agree!"
/>With Reactions
Show emoji reaction pills below the bubble with dynamic emoji items:
<ChatMessage
reaction
reactionProps={{
reactions: [
{ emoji: '❤️', count: 3 },
{ emoji: '🎉', count: 1 },
{ emoji: '🔥' },
]
}}
message="Great idea!"
/>Sub-Components
ChatReply
Inline reply preview shown inside the chat bubble:
import { ChatReply } from '@versaui/ui/components/ChatMessage';
<ChatReply type="text" senderName="Alice" content="Original message" />
<ChatReply type="icon" content="Missed call" />
<ChatReply type="image" imageUrl="/photo.jpg" />
<ChatReply type="file" fileName="report.pdf" fileSize="412 KB" />Reactions
Emoji reaction cluster pill beneath a message. Supports any custom emoji array, adaptive padding, fixed 20×20px frame centering with baseline typography, and scale pop transitions:
import { Reactions } from '@versaui/ui/components/ChatMessage';
// Dynamic reaction data
<Reactions
reactions={[
{ emoji: '❤️', count: 4 },
{ emoji: '👍🏻', count: 2 },
{ emoji: '🚀' },
]}
onReactionClick={(reaction, index) => console.log('Clicked:', reaction)}
onAddClick={() => console.log('Add reaction')}
/>
// Single emoji (count omitted automatically)
<Reactions reactions={[{ emoji: '❤️' }]} />ReactButton
Smiley icon trigger that opens an elevated Material popover with quick-pick emojis and spring animations:
import { ReactButton } from '@versaui/ui/components/ChatMessage';
<ReactButton
onReact={(emoji) => console.log('Reacted with:', emoji)}
onMoreClick={() => console.log('More clicked')}
/>Accessibility
- Messages use semantic HTML with appropriate ARIA labels
- Reactions use
aria-labeldescribing the emoji content - React button uses
aria-expandedand click-outside listeners to manage popover visibility - Material popover menu uses
role="menu"withrole="menuitem"for emoji options - More options button has descriptive
aria-label - Reply preview uses
role="blockquote"with attribution
Chat Message Props
| Prop | Type | Default | Description |
|---|---|---|---|
sender | 'user' | 'other' | 'other' | Message sender determining alignment, colors, and layout. |
messageType | 'text' | 'icon' | 'image' | 'file' | 'text' | Content type of the message. |
state | 'default' | 'hovered' | 'default' | Controlled visual state. Auto-detected via hover when unset. |
avatar | boolean | true | Show avatar (other sender only). |
name | boolean | true | Show sender name (other sender only). |
senderName | string | 'Adam Smith' | Display name of the sender. |
message | string | 'Can you provide me with more details on the project?' | Message text content. |
timestamp | string | '06:04 AM' | Timestamp string displayed beside the message. |
reply | boolean | false | Show inline reply preview. |
replyProps | ChatReplyProps | — | Props passed to the ChatReply sub-component. |
reaction | boolean | false | Show reaction pills below the bubble. |
reactionProps | ReactionsProps | — | Props passed to the Reactions sub-component. |
imageUrl | string | — | Image URL for image-type messages. |
changeIcon | ReactNode | — | Custom icon for icon-type messages. |
iconLabel | string | 'Missed call' | Label text for icon-type messages. |
fileName | string | 'Sample file.pdf' | File name for file-type messages. |
fileSize | string | '412 KB' | File size for file-type messages. |
avatarPerson | AvatarPerson | 'adam-smith' | Predefined avatar person key from the design system. |
onMoreClick | () => void | — | More options button click handler. |
onReact | (emoji: string) => void | — | Emoji reaction selection handler. |
className | string | '' | Additional CSS classes. |
Chat Reply Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | 'text' | 'icon' | 'image' | 'file' | 'text' | Content type of the replied-to message. |
senderName | string | 'Adam Smith' | Original sender display name. |
content | string | 'Can you provide me with more details...' | Original text or description. |
imageUrl | string | — | Image URL for image-type replies. |
fileName | string | 'Sample file.pdf' | File name for file-type replies. |
fileSize | string | '412 KB' | File size for file-type replies. |
icon | ReactNode | — | Custom icon for icon-type replies. |
className | string | '' | Additional CSS classes. |
Reactions Props
| Prop | Type | Default | Description |
|---|---|---|---|
reactions | ReactionData[] | — | Custom array of reaction items ({ emoji, count, id, ariaLabel }). |
variant | '1' | '2' | '2+' | '2+' | Preset reaction variant when custom reactions are not provided. |
interactive | boolean | true | Whether reaction emojis and the add button are clickable. |
onReactionClick | (reaction: ReactionData, index: number) => void | — | Called when a reaction emoji is clicked. |
onAddClick | () => void | — | Called when the add (+) reaction button is clicked. |
renderEmoji | (emoji: string, item: ReactionData) => ReactNode | — | Custom renderer for individual emoji elements. |
className | string | '' | Additional CSS classes. |
React Button Props
| Prop | Type | Default | Description |
|---|---|---|---|
state | 'default' | 'hovered' | 'selected' | 'default' | Controlled visual state. |
isOpen | boolean | — | Controlled open state for the reaction options overlay. |
onOpenChange | (open: boolean) => void | — | Callback fired when overlay opens or closes. |
quickEmojis | string[] | ['❤️', '🙂', '👍🏻', '🎉', '😔'] | List of quick-pick emojis shown in the popover overlay. |
onReact | (emoji: string) => void | — | Called when an emoji is selected from the overlay. |
onMoreClick | () => void | — | Called when the more (+) button in the overlay is clicked. |
className | string | '' | Additional CSS classes. |