Chat

Chat Message

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.

Free, MIT licenseOpen in Browse
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 }

Installation

pnpm dlx shadcn add @01orbital/chat-message
npx shadcn add @01orbital/chat-message
yarn shadcn add @01orbital/chat-message
bunx --bun shadcn add @01orbital/chat-message

Free 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.

Usage

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>
  )
}

API reference

Prop

Type

It also accepts the props of the Base UI part it wraps, and the HTML attributes of the element it renders.

Dependencies

npm: cn. The CLI installs them for you.

Registry pieces it installs with it:

  • bubble
  • message