Docs
Shadcn

Message 消息

组织消息头像、正文、页眉和页脚区域。

Preview
Scopify 助手
根据你的最近播放,推荐从《十一月的萧邦》开始。
刚刚 · 推荐已生成
Code
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/styles/radix-rhea/ui/avatar"
import {
  Bubble,
  BubbleContent,
  BubbleGroup,
  BubbleReactions,
} from "@/styles/radix-rhea/ui/bubble"
import { Marker, MarkerContent } from "@/styles/radix-rhea/ui/marker"
import {
  Message,
  MessageAvatar,
  MessageContent,
  MessageFooter,
} from "@/styles/radix-rhea/ui/message"

export function MessageDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-6 py-12">
      <Message align="end">
        <MessageAvatar>
          <Avatar>
            <AvatarImage src="/avatars/10.png" alt="@me" />
            <AvatarFallback>ME</AvatarFallback>
          </Avatar>
        </MessageAvatar>
        <MessageContent>
          <Bubble>
            <BubbleContent>Deploying to prod real quick.</BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <Message>
        <MessageAvatar>
          <Avatar>
            <AvatarImage src="/avatars/02.png" alt="@rabbit" />
            <AvatarFallback>R</AvatarFallback>
          </Avatar>
        </MessageAvatar>
        <MessageContent>
          <Bubble variant="muted">
            <BubbleContent>It&apos;s 4:55 PM. On a Friday.</BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <Message align="end">
        <MessageAvatar>
          <Avatar>
            <AvatarImage src="/avatars/10.png" alt="@me" />
            <AvatarFallback>ME</AvatarFallback>
          </Avatar>
        </MessageAvatar>
        <MessageContent>
          <Bubble>
            <BubbleContent>It&apos;s a one-line change.</BubbleContent>
          </Bubble>
          <MessageFooter>Delivered</MessageFooter>
        </MessageContent>
      </Message>
      <Message>
        <MessageAvatar>
          <Avatar>
            <AvatarImage src="/avatars/02.png" alt="@rabbit" />
            <AvatarFallback>R</AvatarFallback>
          </Avatar>
        </MessageAvatar>
        <MessageContent>
          <BubbleGroup>
            <Bubble variant="muted">
              <BubbleContent>
                It&apos;s always a one-line change 😭.
              </BubbleContent>
            </Bubble>
            <Bubble variant="muted">
              <BubbleContent>Alright, let me take a look.</BubbleContent>
              <BubbleReactions aria-label="Reactions: thumbs up">
                <span>👍</span>
              </BubbleReactions>
            </Bubble>
          </BubbleGroup>
        </MessageContent>
      </Message>
      <Marker role="status">
        <MarkerContent className="shimmer">
          <span className="font-medium">Oliver</span> is typing...
        </MarkerContent>
      </Marker>
    </div>
  )
}

The Message component lays out a single message in a conversation. It handles the avatar, alignment, header, and footer around the message surface.

For AI apps, you can render reasoning steps, tool calls and assistant messages using the Message component.

Scopify 说明

本页按 shadcn/ui 官方 Radix UI 组件文档的章节与示例完整整理。示例预览使用 Scopify 当前的 @scopify/ui/shadcn 实现;导入路径、主题 Token 和行为差异以本仓库源码为准。

Installation

npx shadcn@latest add message

Usage

import { Avatar, AvatarFallback, AvatarImage } from "@scopify/ui/shadcn/components/avatar"
import { Bubble, BubbleContent } from "@scopify/ui/shadcn/components/bubble"
import { Message, MessageAvatar, MessageContent } from "@scopify/ui/shadcn/components/message"
<Message>
  <MessageAvatar>
    <Avatar>
      <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
      <AvatarFallback>CN</AvatarFallback>
    </Avatar>
  </MessageAvatar>
  <MessageContent>
    <Bubble>
      <BubbleContent>How can I help you today?</BubbleContent>
    </Bubble>
  </MessageContent>
</Message>

Note: Message owns the row layout—avatar, alignment, header, and footer. Render the visible message surface inside it with Bubble. For the scroll container around a conversation, use MessageScroller.

Composition

Use the following composition to build a message:

Message
├── MessageAvatar
└── MessageContent
    ├── MessageHeader
    ├── Bubble
    └── MessageFooter

Use MessageGroup to stack consecutive messages from the same sender:

MessageGroup
├── Message
└── Message

Features

  • Start and end alignment for sender and receiver rows via the align prop
  • Avatar slot that anchors to the bottom of the message and stays clear of the footer
  • Header and footer slots for sender names, status, and message actions
  • Footer follows the message side; actions stay aligned on align="end" rows
  • Group wrapper for stacking consecutive messages from the same sender
  • Customizable styling through the className prop on every part

Avatar

Use MessageAvatar to render an avatar next to the message. Set align="end" on the message to align the avatar to the end of the message.

Preview
Scopify 助手
根据你的最近播放,推荐从《十一月的萧邦》开始。
刚刚 · 推荐已生成
Code
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/styles/radix-rhea/ui/avatar"
import {
  Bubble,
  BubbleContent,
  BubbleGroup,
} from "@/styles/radix-rhea/ui/bubble"
import {
  Message,
  MessageAvatar,
  MessageContent,
} from "@/styles/radix-rhea/ui/message"

export function MessageAvatarDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-6 py-12">
      <Message>
        <MessageAvatar>
          <Avatar>
            <AvatarImage src="/avatars/03.png" alt="@avatar" />
            <AvatarFallback>R</AvatarFallback>
          </Avatar>
        </MessageAvatar>
        <MessageContent>
          <Bubble variant="muted">
            <BubbleContent>
              The build failed during dependency installation.
            </BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <Message align="end">
        <MessageAvatar>
          <Avatar>
            <AvatarImage src="/avatars/10.png" alt="@avatar" />
            <AvatarFallback>R</AvatarFallback>
          </Avatar>
        </MessageAvatar>
        <MessageContent>
          <Bubble>
            <BubbleContent>Can you share the exact error?</BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <Message>
        <MessageAvatar>
          <Avatar>
            <AvatarImage src="/avatars/03.png" alt="@avatar" />
            <AvatarFallback>R</AvatarFallback>
          </Avatar>
        </MessageAvatar>
        <MessageContent>
          <BubbleGroup>
            <Bubble variant="muted">
              <BubbleContent>Here&apos;s the error from the logs</BubbleContent>
            </Bubble>
            <Bubble variant="muted">
              <BubbleContent>
                Something went wrong with the build. The libraries are not
                installed correctly. Try running the build again.
              </BubbleContent>
            </Bubble>
          </BubbleGroup>
        </MessageContent>
      </Message>
    </div>
  )
}
alignDescription
startAlign the message to the start of the conversation.
endAlign the message to the end of the conversation.

Group

Use MessageGroup to stack consecutive messages from the same sender. Render an empty MessageAvatar on the earlier messages to keep them aligned with the avatar on the last one.

Preview
Scopify 助手
根据你的最近播放,推荐从《十一月的萧邦》开始。
刚刚 · 推荐已生成
Code
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/styles/radix-rhea/ui/avatar"
import { Bubble, BubbleContent } from "@/styles/radix-rhea/ui/bubble"
import {
  Message,
  MessageAvatar,
  MessageContent,
  MessageGroup,
} from "@/styles/radix-rhea/ui/message"

export function MessageGroupDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-6 py-12">
      <MessageGroup>
        <Message>
          <MessageAvatar />
          <MessageContent>
            <Bubble variant="muted">
              <BubbleContent>I checked the registry addresses.</BubbleContent>
            </Bubble>
          </MessageContent>
        </Message>
        <Message>
          <MessageAvatar>
            <Avatar>
              <AvatarImage src="/avatars/02.png" alt="@avatar" />
              <AvatarFallback>CN</AvatarFallback>
            </Avatar>
          </MessageAvatar>
          <MessageContent>
            <Bubble variant="muted">
              <BubbleContent>
                The component and example JSON now live under the UI registry.
              </BubbleContent>
            </Bubble>
          </MessageContent>
        </Message>
      </MessageGroup>
    </div>
  )
}

Use MessageHeader for a sender name and MessageFooter for metadata such as a delivery or read status.

Preview
Scopify 助手
根据你的最近播放,推荐从《十一月的萧邦》开始。
刚刚 · 推荐已生成
Code
import { Bubble, BubbleContent } from "@/styles/radix-rhea/ui/bubble"
import {
  Message,
  MessageContent,
  MessageFooter,
  MessageHeader,
} from "@/styles/radix-rhea/ui/message"

export function MessageHeaderFooterDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-8 py-12">
      <Message>
        <MessageContent>
          <MessageHeader>Olivia</MessageHeader>
          <Bubble variant="muted">
            <BubbleContent>I already checked the logs.</BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <Message align="end">
        <MessageContent>
          <Bubble>
            <BubbleContent>
              Send the report to the team. Ping @shadcn if you need help.
            </BubbleContent>
          </Bubble>
          <MessageFooter>
            <div>
              Read <span className="font-normal">Yesterday</span>
            </div>
          </MessageFooter>
        </MessageContent>
      </Message>
    </div>
  )
}

Actions

Place message-level actions in MessageFooter, such as copy, retry, or feedback buttons.

Preview
Scopify 助手
根据你的最近播放,推荐从《十一月的萧邦》开始。
刚刚 · 推荐已生成
Code
import {
  CopyIcon,
  RefreshCcwIcon,
  ThumbsDownIcon,
  ThumbsUpIcon,
} from "lucide-react"

import { Bubble, BubbleContent } from "@/styles/radix-rhea/ui/bubble"
import { Button } from "@/styles/radix-rhea/ui/button"
import {
  Message,
  MessageContent,
  MessageFooter,
} from "@/styles/radix-rhea/ui/message"

export function MessageActionsDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-8 py-12">
      <Message>
        <MessageContent>
          <Bubble variant="muted">
            <BubbleContent>
              The install failure is coming from the workspace package.
            </BubbleContent>
          </Bubble>
          <MessageFooter>
            <Button variant="ghost" size="icon" aria-label="Copy" title="Copy">
              <CopyIcon />
            </Button>
            <Button variant="ghost" size="icon" aria-label="Like" title="Like">
              <ThumbsUpIcon />
            </Button>
            <Button
              variant="ghost"
              size="icon"
              aria-label="Dislike"
              title="Dislike"
            >
              <ThumbsDownIcon />
            </Button>
          </MessageFooter>
        </MessageContent>
      </Message>
      <Message align="end">
        <MessageContent>
          <Bubble>
            <BubbleContent>Okay drop me a link. Taking a look...</BubbleContent>
          </Bubble>
          <MessageFooter className="gap-2">
            <span className="font-normal text-destructive">Failed to send</span>
            <Button
              variant="ghost"
              size="icon-xs"
              title="Retry"
              aria-label="Retry"
            >
              <RefreshCcwIcon />
            </Button>
          </MessageFooter>
        </MessageContent>
      </Message>
    </div>
  )
}

Attachment

Preview
Scopify 助手
根据你的最近播放,推荐从《十一月的萧邦》开始。
刚刚 · 推荐已生成
Code
"use client"

import { DownloadIcon, FileTextIcon } from "lucide-react"

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from "@/styles/radix-rhea/ui/attachment"
import { Bubble, BubbleContent } from "@/styles/radix-rhea/ui/bubble"
import { Message, MessageContent } from "@/styles/radix-rhea/ui/message"

export function MessageAttachmentDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-8 py-12">
      <Message align="end">
        <MessageContent>
          <Attachment orientation="vertical">
            <AttachmentMedia variant="image">
              <img
                src="https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=900&auto=format&fit=crop&q=80"
                alt="Workspace"
              />
            </AttachmentMedia>
          </Attachment>
          <Bubble>
            <BubbleContent>
              Here&apos;s the image. Can you add it to the PDF? Use it for the
              cover page.
            </BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <Message>
        <MessageContent>
          <Bubble variant="muted">
            <BubbleContent>
              Done. Here&apos;s the PDF with the image added as the cover page.
            </BubbleContent>
          </Bubble>
          <Attachment>
            <AttachmentMedia>
              <FileTextIcon />
            </AttachmentMedia>
            <AttachmentContent>
              <AttachmentTitle>sales-dashboard.pdf</AttachmentTitle>
              <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>
            </AttachmentContent>
            <AttachmentActions>
              <AttachmentAction
                type="button"
                title="Download"
                aria-label="Download"
                size="icon-sm"
                variant="secondary"
              >
                <DownloadIcon />
              </AttachmentAction>
            </AttachmentActions>
          </Attachment>
        </MessageContent>
      </Message>
      <Message align="end">
        <MessageContent>
          <Bubble>
            <BubbleContent>Thanks. Looks good.</BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
    </div>
  )
}

Accessibility

Message is a presentational layout wrapper. Accessibility comes from the content you place inside it.

Label icon-only actions

Action buttons in MessageFooter are usually icon-only, so give each one an aria-label.

<MessageFooter>
  <Button variant="ghost" size="icon" aria-label="Copy">
    <CopyIcon />
  </Button>
</MessageFooter>

Status updates

For in-progress messages, use a Marker with role="status" so assistive tech announces the update as it appears.

<Message>
  <Marker role="status">
    <MarkerIcon>
      <Spinner />
    </MarkerIcon>
    <MarkerContent>Checking the logs...</MarkerContent>
  </Marker>
</Message>

API Reference

Message

The message row wrapper.

PropTypeDefaultDescription
align"start" | "end""start"The alignment of the message in the conversation.
classNamestring-Additional classes to apply to the row.

MessageGroup

Groups consecutive messages from the same sender.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the group root.

MessageAvatar

The avatar slot, aligned to the bottom of the message. When the message has a MessageFooter, the avatar shifts up to stay aligned with the message surface instead of the footer.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the avatar slot.

MessageContent

Wraps the header, message surface, and footer.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the content slot.

MessageHeader

Displays content above the message, such as a sender name. Stays aligned to the start regardless of align.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the header.

MessageFooter

Displays content below the message, such as status or actions. Aligns to the message side.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the footer.

本页内容