Component
Message
One message in a conversation: a 32px avatar tile, the sender and time in 14px, the bubbles, and a 12px muted footer.
npx -y github:anwarlemu/inkbox-brand-cli#release add messageUsage
Build a conversation from Message rows inside a MessageGroup: MessageAvatar with an initial or a picture, MessageContent with a MessageHeader (the name, then a time element), the Bubble pieces and an optional MessageFooter (Delivered, Read). Set align end for the person's own messages; the row mirrors to the right.
Avoid
- Round avatars
- A bold sender name
- Timestamps darker than the muted purple
Exports: Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader. Read the examples for composition and props.
Example
import { CheckIcon } from "@phosphor-icons/react/ssr";import { Bubble, BubbleContent, BubbleGroup } from "@/components/ui/bubble";import { Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader } from "@/components/ui/message";export function Preview() { return ( <MessageGroup className="mx-auto w-full max-w-140"> <Message> <MessageAvatar>R</MessageAvatar> <MessageContent> <MessageHeader> Ray Liao <time dateTime="2026-08-08T19:16">7:16 PM</time> </MessageHeader> <BubbleGroup> <Bubble variant="secondary"> <BubbleContent>Can your agent publish its Agent Card?</BubbleContent> </Bubble> <Bubble variant="secondary"> <BubbleContent>Then other organizations can find it and delegate to it.</BubbleContent> </Bubble> </BubbleGroup> </MessageContent> </Message> <Message align="end"> <MessageAvatar>A</MessageAvatar> <MessageContent> <MessageHeader> @acme <time dateTime="2026-08-08T19:18">7:18 PM</time> </MessageHeader> <Bubble align="end"> <BubbleContent>Done. It is listed in the public directory now.</BubbleContent> </Bubble> <MessageFooter> <CheckIcon weight="bold" /> Delivered </MessageFooter> </MessageContent> </Message> </MessageGroup> );}export function AgentOutput() { return ( <Message className="mx-auto max-w-140"> <MessageAvatar> {/* eslint-disable-next-line @next/next/no-img-element */} <img src="/octopus.png" alt="" className="bg-subtle" /> </MessageAvatar> <MessageContent> <MessageHeader> test-agent <time dateTime="2026-08-08T19:20">7:20 PM</time> </MessageHeader> <Bubble variant="ghost"> <BubbleContent>I sent the first email from test-agent-233@inkboxmail.com and saved my identity for future sessions. The tunnel at test-agent-233.inkboxwire.com is idle until a request arrives.</BubbleContent> </Bubble> </MessageContent> </Message> );}Source
import * as React from "react";import { cn } from "@/lib/utils";// One message in an agent conversation (Figma 427:2715, the inbox and iMessage): a 32px avatar tile (the initial on// the avatar blue, 6px corners, as the rail's "R"), the sender's name in 14px ink with the time in 14px muted beside// it, the bubbles, and a 12px muted footer (Delivered). Outgoing messages (align end) mirror to the right.function MessageGroup({ className, ...props }: React.ComponentProps<"div">) { return <div data-slot="message-group" className={cn("flex min-w-0 flex-col gap-4", className)} {...props} />;}function Message({ className, align = "start", ...props }: React.ComponentProps<"div"> & { align?: "start" | "end" }) { return <div data-slot="message" data-align={align} className={cn("group/message relative flex w-full min-w-0 gap-2.5 font-sans text-body data-[align=end]:flex-row-reverse", className)} {...props} />;}function MessageAvatar({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="message-avatar" className={cn( "flex size-8 shrink-0 items-center justify-center self-end overflow-hidden rounded-sm bg-avatar text-body leading-tight font-medium text-white group-has-data-[slot=message-footer]/message:-translate-y-6 *:[img]:size-full *:[img]:object-cover", className, )} {...props} /> );}function MessageContent({ className, ...props }: React.ComponentProps<"div">) { return <div data-slot="message-content" className={cn("flex w-full min-w-0 flex-col gap-1.5 break-words group-data-[align=end]/message:items-end", className)} {...props} />;}/** The sender and the time: "Ray Liao" in ink, "Yesterday" muted, as an inbox row. */function MessageHeader({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="message-header" className={cn("flex max-w-full min-w-0 items-baseline gap-2 px-1 text-body leading-tight text-fg group-data-[align=end]/message:flex-row-reverse [&_time]:text-fg-muted group-has-data-[variant=ghost]/message:px-0", className)} {...props} /> );}function MessageFooter({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="message-footer" className={cn("flex max-w-full min-w-0 items-center gap-1 px-1 text-caption leading-tight text-fg-muted group-data-[align=end]/message:justify-end group-has-data-[variant=ghost]/message:px-0 [&_svg:not([class*='size-'])]:size-3.5", className)} {...props} /> );}export { Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader };Reference: Extrapolated from the Inkbox console (Figma 427:2715): the inbox rows (sender in ink, date muted) and the rail's initial tile.
