A single chat turn: the user's message in a muted bubble aligned to the end, the assistant's as bare text at the start. Built on Message and Bubble.
import * as React from "react"
import { cn } from "cn"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Message } from "@/components/ui/message"
/**
* One chat message. The person's own messages sit on the right in a soft
* bubble; the assistant's sit on the left as bare text.
*/
function ChatMessage({
variant = "theirs",
className,
...props
}: React.ComponentProps<typeof BubbleContent> & {
/** "mine" for the person's message (right, bubbled), "theirs" for the reply (left, bare). */
variant?: "mine" | "theirs"
}) {
const mine = variant === "mine"
return (
// data-slot rides the row: Bubble's variants target data-slot=bubble-content
<Message data-slot="chat-message" align={mine ? "end" : "start"}>
<Bubble
variant={mine ? "muted" : "ghost"}
align={mine ? "end" : "start"}
className={mine ? "max-w-[85%]" : undefined}
>
<BubbleContent
className={cn(
"whitespace-pre-wrap text-foreground",
mine ? "rounded-2xl px-4 py-2.5 text-sm/6" : "text-sm/7",
className
)}
{...props}
/>
</Bubble>
</Message>
)
}
export { ChatMessage }
pnpm dlx shadcn add @01orbital/chat-messagenpx shadcn add @01orbital/chat-messageyarn shadcn add @01orbital/chat-messagebunx --bun shadcn add @01orbital/chat-messageFree under the MIT license. Add the @01orbital registry to components.json once (install guide), then run the command.
It lands at components/chat/chat-message.tsx, as source you own and edit.
import { ChatMessage } from "@/components/chat/chat-message"
// Every word on the page. Change copy here, not in the markup.
const COPY = {
body: "My words sit in a filled bubble, on the right.",
body2:
"The assistant answers in bare prose on the ground, using the full column — no bubble, no border. The whitespace is the boundary.",
} as const
export default function ChatMessageDemo() {
return (
<div className="flex max-w-md flex-col gap-10">
<ChatMessage variant="mine">{COPY.body}</ChatMessage>
<ChatMessage variant="theirs">{COPY.body2}</ChatMessage>
</div>
)
}Prop
Type
It also accepts the props of the Base UI part it wraps, and the HTML attributes of the element it renders.
npm: cn. The CLI installs them for you.
Registry pieces it installs with it:
bubblemessageChat Inline Component Card
Frame for a live registry component inside a chat reply: a mono caption with category, name and open link over a scaled-down preview. Includes helpers for [[name]] citations.
Chat Response
Renders an assistant's streamed text, splitting fenced code into Chat Code Blocks and setting inline backtick code in mono, with a slot for a cursor at the end.