Skip to content

ChatBubble ​

A chat UI component supporting three message role variants. Bold-bordered bubbles with shadow offsets give the conversation interface a distinctive personality.

Demo ​

Preview
对话开始于 2025-06-01
AL
Alex
嘿!这个新粗野主义 UI 库看起来太赞了!
14:01
我
我
对吧!粗边框 + 硬朗阴影,直接把普通组件卷死。
14:02
AL
Alex
Kanban + TreeView 这两个复合组件是重头戏 🔥
14:03
我
我
还有 Carousel!Embla 驱动的,性能拉满 💪
14:03

颜色(仅 sent 生效)

AL
Alex
received 不受 color 影响
14:00
我
我
default 配色
14:01
我
我
primary 配色
14:02
我
我
accent 配色
14:03

尺寸

我
我
sm 小号气泡
14:00
我
我
default 默认气泡
14:01
我
我
lg 大号气泡
14:02

Installation ​

pnpm dlx brutx-vue@latest add chat-bubble

Usage ​

vue
<script setup>
import { ChatBubble } from 'brutx-ui-vue'

const message = {
    id: '1',
    variant: 'received',
    name: 'Alex',
    content: 'This UI library is amazing!',
    timestamp: '14:01',
}
</script>

<template>
    <div class="flex flex-col gap-3">
        <ChatBubble :message="{ id: '1', variant: 'received', name: 'Alex', content: 'Hello!' }" />
        <ChatBubble :message="{ id: '2', variant: 'sent', content: 'Hey!' }" />
        <ChatBubble :message="{ id: '3', variant: 'system', content: 'Conversation started' }" />
    </div>
</template>

Color Variants ​

The color prop switches the background color scheme for variant="sent" bubbles, combining orthogonally with variant (e.g., variant="sent" color="accent"). received and system bubbles are not affected by color.

vue
<script setup>
import { ChatBubble } from 'brutx-ui-vue'
</script>

<template>
    <div class="flex flex-col gap-3">
        <ChatBubble :message="{ id: '1', variant: 'sent', content: 'Default color', timestamp: '14:00' }" color="default" />
        <ChatBubble :message="{ id: '2', variant: 'sent', content: 'Primary color', timestamp: '14:01' }" color="primary" />
        <ChatBubble :message="{ id: '3', variant: 'sent', content: 'Accent color', timestamp: '14:02' }" color="accent" />
    </div>
</template>

Variants ​

Message Roles ​

VariantStylePurpose
receivedLeft-aligned, background colorMessages from others
sentRight-aligned, primary color backgroundMessages sent by yourself
systemCentered, italic, dashed border, no shadow, forced text-xsSystem notifications

Note: The system variant ignores the size prop and always uses text-xs. It also hides the avatar and sender name.

Colors ​

ColorDescription
defaultDefault color scheme; sent bubbles use primary color (brutal-primary) background
primaryPrimary color scheme; sent bubbles use primary color (brutal-primary) background with primary color shadow (shadow-brutal-primary)
accentAccent color scheme; sent bubbles use accent color (brutal-accent) background

Sizes ​

The size prop controls bubble padding, text size, and avatar dimensions simultaneously.

SizeBubble PaddingText SizeAvatar Size
smpx-3 py-1.5text-xsw-6 h-6
defaultpx-4 py-2.5text-smw-8 h-8
lgpx-5 py-3.5text-basew-10 h-10
vue
<script setup>
import { ChatBubble } from 'brutx-ui-vue'
</script>

<template>
    <div class="flex flex-col gap-3">
        <ChatBubble :message="{ id: '1', variant: 'sent', name: 'Me', content: 'Small bubble', timestamp: '14:00' }" size="sm" />
        <ChatBubble :message="{ id: '2', variant: 'sent', name: 'Me', content: 'Default bubble', timestamp: '14:01' }" size="default" />
        <ChatBubble :message="{ id: '3', variant: 'sent', name: 'Me', content: 'Large bubble', timestamp: '14:02' }" size="lg" />
    </div>
</template>

Sub-components ​

ChatContainer ​

A chat message container with time-based grouping support:

vue
<script setup>
import { ChatContainer } from 'brutx-ui-vue'

const messages = [
    { id: '1', variant: 'received', name: 'Alex', content: 'Hello!', timestamp: new Date() },
    { id: '2', variant: 'sent', content: 'Hey!', timestamp: new Date(), status: 'read' },
]
</script>

<template>
    <ChatContainer :messages="messages" group-by-time show-status />
</template>

Data Types ​

ChatMessage ​

ts
interface ChatMessage {
    id: string                        // Unique message identifier (required)
    content: string                   // Message text content (required)
    variant?: 'sent' | 'received' | 'system'  // Message variant, defaults to 'received'
    avatar?: string                   // Avatar image URL; falls back to initials on load failure
    name?: string                     // Sender name, used for display and initial generation
    timestamp?: string | Date         // Timestamp; strings are displayed directly, Date objects are formatted via dateFormat or toLocaleString
    status?: MessageStatus            // Message status, only effective for variant="sent"
}

MessageStatus ​

ts
type MessageStatus = 'sending' | 'sent' | 'delivered' | 'read' | 'failed'
StatusIconStyle
sendingSpinning loader iconSemi-transparent + rotation animation
sentSingle checkSemi-transparent
deliveredDouble checkSlightly darker
readDouble checkTheme primary color
failedWarning iconDestructive color

Note: status is a closed union type at the type level, but message data usually comes from backend APIs and may carry values outside the enum at runtime; in that case ChatBubble silently ignores the status (no status icon is rendered) without throwing an error.

API Reference ​

ChatBubble

ChatBubble — Props
NameTypeDefaultDescription
message
Required
ChatMessage
—
The chat message to display, including its id, content, role, timestamp, and optional avatar and status data.
Details

Declared type

ChatMessage

Referenced types

ChatMessage
Type definition — ChatMessage
export interface ChatMessage {
    id: string
    content: string
    /** 消息角色,默认 'received' */
    variant?: 'sent' | 'received' | 'system'
    /** 头像图片 URL;加载失败时回退为首字母缩写 */
    avatar?: string
    /** 发送者姓名,用于展示名称与生成首字母缩写 */
    name?: string
    /**
     * 时间戳。
     * - `string`:按原样直接展示(不做格式化,传入方负责提供展示格式);
     * - `Date`:按 `dateFormat` 或 `toLocaleString` 格式化。
     */
    timestamp?: string | Date
    /**
     * 消息状态,**仅对 `variant === 'sent'` 生效**;received/system 携带时会被静默忽略。
     */
    status?: MessageStatus
}

Default value details

Not declared
—

Source

ChatBubble.vue:12

packages/ui/src/components/chat-bubble/ChatBubble.vue

color
"accent" | "default" | "primary" | undefined
"default"
Sets the color of sent-role bubbles; received and system roles are unaffected.
Details

Declared type

NonNullable<"default" | "primary" | "accent" | null | undefined> | undefined

Literal values

  • "accent"
  • "default"
  • "primary"

Default value details

Resolved value
"default"

Source

ChatBubble.vue:14

packages/ui/src/components/chat-bubble/ChatBubble.vue

size
"default" | "lg" | "sm" | undefined
"default"
Sets bubble padding and text size and adjusts the avatar size to match.
Details

Declared type

NonNullable<"default" | "lg" | "sm" | null | undefined> | undefined

Literal values

  • "default"
  • "lg"
  • "sm"

Default value details

Resolved value
"default"

Source

ChatBubble.vue:15

packages/ui/src/components/chat-bubble/ChatBubble.vue

showAvatar
boolean | undefined
true
Controls the avatar area; system-role messages never display an avatar.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Resolved value
true

Source

ChatBubble.vue:16

packages/ui/src/components/chat-bubble/ChatBubble.vue

showStatus
boolean | undefined
true
Controls the send-status icon; the status icon is used only for sent-role messages.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Resolved value
true

Source

ChatBubble.vue:17

packages/ui/src/components/chat-bubble/ChatBubble.vue

showTimestamp
boolean | undefined
true
Controls whether the message timestamp is displayed.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Resolved value
true

Source

ChatBubble.vue:18

packages/ui/src/components/chat-bubble/ChatBubble.vue

dateFormat
((date: Date) => string) | undefined
undefined
Custom formatter for the message date and time; Date.toLocaleString is used when omitted.
Details

Declared type

((date: Date) => string) | undefined

Default value details

Resolved value
undefined

Source

ChatBubble.vue:19

packages/ui/src/components/chat-bubble/ChatBubble.vue

class
string | undefined
undefined
Custom CSS classes added to the message bubble root element.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined

Source

ChatBubble.vue:20

packages/ui/src/components/chat-bubble/ChatBubble.vue

ChatBubble — Slots
NameSignature or typeDescription
#default
{}
Replaces the message body; message.content is shown when omitted.
Details

Declared type

{}

Source

ChatBubble.vue

packages/ui/src/components/chat-bubble/ChatBubble.vue

ChatContainer

ChatContainer — Props
NameTypeDefaultDescription
messages
Required
ChatMessage[]
—
The ordered array of chat messages to display.
Details

Declared type

ChatMessage[]

Referenced types

ChatMessage
Type definition — ChatMessage
export interface ChatMessage {
    id: string
    content: string
    /** 消息角色,默认 'received' */
    variant?: 'sent' | 'received' | 'system'
    /** 头像图片 URL;加载失败时回退为首字母缩写 */
    avatar?: string
    /** 发送者姓名,用于展示名称与生成首字母缩写 */
    name?: string
    /**
     * 时间戳。
     * - `string`:按原样直接展示(不做格式化,传入方负责提供展示格式);
     * - `Date`:按 `dateFormat` 或 `toLocaleString` 格式化。
     */
    timestamp?: string | Date
    /**
     * 消息状态,**仅对 `variant === 'sent'` 生效**;received/system 携带时会被静默忽略。
     */
    status?: MessageStatus
}

Default value details

Not declared
—

Source

ChatContainer.vue:18

packages/ui/src/components/chat-bubble/ChatContainer.vue

groupByTime
boolean | undefined
false
Groups messages by calendar date and time interval and displays date or time separators.
  • Messages with valid timestamps are sorted in ascending order; messages with missing or unparseable timestamps remain anchored to their input positions and keep their relative order.
  • Date boundaries use the actual year, month, and day rather than dateFormat output. Within one date, adjacent messages more than groupInterval minutes apart start a separate time group.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Resolved value
false

Source

ChatContainer.vue:19

packages/ui/src/components/chat-bubble/ChatContainer.vue

groupInterval
number | undefined
5
The maximum gap between adjacent messages in one time group, in minutes; values below 1 are clamped to 1.
Details

Declared type

number | undefined

Default value details

Resolved value
5

Source

ChatContainer.vue:20

packages/ui/src/components/chat-bubble/ChatContainer.vue

showAvatar
boolean | undefined
true
Controls whether message avatar areas are displayed.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Resolved value
true

Source

ChatContainer.vue:21

packages/ui/src/components/chat-bubble/ChatContainer.vue

showStatus
boolean | undefined
true
Controls whether status icons are displayed for sent messages.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Resolved value
true

Source

ChatContainer.vue:22

packages/ui/src/components/chat-bubble/ChatContainer.vue

showTimestamp
boolean | undefined
true
Controls whether message timestamps are displayed.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Resolved value
true

Source

ChatContainer.vue:23

packages/ui/src/components/chat-bubble/ChatContainer.vue

dateFormat
((date: Date) => string) | undefined
undefined
Formats message times and grouping labels; local date and time formatting is used when omitted.
Details

Declared type

((date: Date) => string) | undefined

Default value details

Resolved value
undefined

Source

ChatContainer.vue:24

packages/ui/src/components/chat-bubble/ChatContainer.vue

class
string | undefined
undefined
Custom CSS classes added to the chat container root element.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined

Source

ChatContainer.vue:25

packages/ui/src/components/chat-bubble/ChatContainer.vue

Note: When groupByTime is true:

  • Messages with a valid timestamp are sorted by timestamp ascending, so unordered input still yields continuous, non-jumping date groups; messages without a valid timestamp (missing, or display-only strings like '14:30', 'yesterday' that cannot be parsed) stay anchored at their original index — they do not sink to the end and keep their relative order;
  • Group boundaries are compared by the real calendar date (year/month/day), decoupled from the display string produced by dateFormat: even if a custom format outputs a time-only dimension (e.g., HH:mm), messages from different days are never merged and same-day messages are never split into separate date groups;
  • Groups are separated by dividers and date labels (today/yesterday/specific date);
  • Within the same date, adjacent messages whose time gap exceeds groupInterval minutes are further split into separate groups that show the specific time (HH:mm, customizable via dateFormat), without repeating the date label.

Accessibility ​

  • ARIA Attributes: ChatContainer uses role="log" for accessibility support
  • Semantics: Message bubbles use semantic structure, supporting correct screen reader narration

Brute force builds.