Chat Bubble

Miles Davis
Rehearsal moved to Thursday, the room at the Blue Note is free from six.
Bring the new charts.
Thursday works. I finished the arrangement for the second set last night.
BjΓΆrk
Oh, the modulation into the bridge is lovely. Can we try it a little slower first?
Miles Davis
About this component

Chat Bubble

A conversation drawn as message groups: consecutive messages from one person within a few minutes gather under one avatar and name, their corners tightening where the bubbles meet so a run reads as one voice. The reader's messages sit on the right in the primary color, everyone else's on the left in one of three tones. Times slide out beside a bubble on hover or focus, the reader's latest message carries its delivery state (sending, sent, delivered, read), reactions sit under a bubble as chips, bare links become links, and a typing bubble of three dots joins the person's group. The whole log is one tab stop with arrow keys between messages.

Usage

The import and the props worth knowing about, in one place.

import { ChatBubble } from "@/components/beste/component/chat-bubble";

<ChatBubble
  me="joni"
  people={[
    { id: "joni", name: "Joni Mitchell" },
    { id: "miles", name: "Miles Davis", avatar: "/avatars/miles.jpg" },
  ]}
  messages={[
    { id: "1", from: "miles", text: "Rehearsal moved to Thursday.", time: "2026-09-27T17:02:00Z" },
    { id: "2", from: "joni", text: "Works for me.", time: "2026-09-27T17:05:00Z", status: "read" },
  ]}
  typing={["miles"]}                                   // a three-dot bubble joins their group
  onReact={(id, emoji) => console.log("React", id, emoji)} // makes reaction chips pressable
  groupWithin={5}                                      // minutes between messages in one group
  tone="outline"                                       // "muted" (default) | "outline" | "ghost"
  size="sm"                                            // "sm" | "default" | "lg"
/>

Keyboard and gestures

Every one of these is in the component already. They are listed because a props table cannot mention a gesture, so nothing else on this page can tell you they exist.

Up / DownMoves between messages once one is focused, showing its time.
Home / EndJumps to the first or last message.

Props

Read from the component's own type, so this cannot drift from what it accepts.

PropTypeDefaultDescription
messages*ChatMessage[]β€”
people*ChatPerson[]β€”Everyone who appears in `messages` or `typing`.
me*stringβ€”Id of the reader; their messages sit on the right.
typingstring[]β€”Ids of people typing right now, shown as a bubble of three dots.
groupWithinnumberβ€”Messages further apart than this, in minutes, start a new group.
showNamesbooleanβ€”Show names above other people's groups.
onReact(messageId: string, emoji: string) => voidβ€”Called when a reaction chip is pressed. Without it the chips are read-only.
formatTime(date: Date) => stringβ€”How a message time reads. Runs on the client only, so time zones never split server and client.
tone"muted" | "outline" | "ghost"β€”
size"sm" | "default" | "lg"β€”
classNamestringβ€”
aria-labelstringβ€”