- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Form
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Pin Input
- Popover
- Progress
- Radio Group
- Range Calendar
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Stepper
- Switch
- Table
- Tabs
- Tags Input
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
- Typography
<script setup lang="ts">
import {
Avatar,
AvatarFallback,
AvatarImage,
} from '@/components/ui/avatar'
import {
Bubble,
BubbleContent,
BubbleGroup,
BubbleReactions,
} from '@/components/ui/bubble'
import { Marker, MarkerContent } from '@/components/ui/marker'
import { Message, MessageAvatar, MessageContent, MessageFooter } from '@/components/ui/message'
</script>
<template>
<div class="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'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'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'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 class="shimmer">
<span class="font-medium">Oliver</span> is typing...
</MarkerContent>
</Marker>
</div>
</template>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.
Installation
pnpm dlx shadcn-vue@latest add message
Usage
<script setup lang="ts">
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Message, MessageAvatar, MessageContent } from "@/components/ui/message"
</script>
<template>
<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>
</template>Note: Message owns the row layout—avatar, alignment, header, and footer.
Render the visible message surface inside it with
Bubble.
Composition
Use the following composition to build a message:
Message
├── MessageAvatar
└── MessageContent
├── MessageHeader
├── Bubble
└── MessageFooterUse MessageGroup to stack consecutive messages from the same sender:
MessageGroup
├── Message
└── MessageFeatures
- Start and end alignment for sender and receiver rows via the
alignprop - 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
- Polymorphic wrappers via
asoras-child - Customizable styling through the
classprop on every part
Examples
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.
<script setup lang="ts">
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Bubble, BubbleContent, BubbleGroup } from '@/components/ui/bubble'
import { Message, MessageAvatar, MessageContent } from '@/components/ui/message'
</script>
<template>
<div class="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'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>
</template>| align | Description |
|---|---|
start | Align the message to the start of the conversation. |
end | Align 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.
<script setup lang="ts">
import {
Avatar,
AvatarFallback,
AvatarImage,
} from '@/components/ui/avatar'
import { Bubble, BubbleContent } from '@/components/ui/bubble'
import { Message, MessageAvatar, MessageContent, MessageGroup } from '@/components/ui/message'
</script>
<template>
<div class="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>
</template>Header and Footer
Use MessageHeader for a sender name and MessageFooter for metadata such as a delivery or read status.
<script setup lang="ts">
import { Bubble, BubbleContent } from '@/components/ui/bubble'
import { Message, MessageContent, MessageFooter, MessageHeader } from '@/components/ui/message'
</script>
<template>
<div class="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 class="font-normal">Yesterday</span>
</div>
</MessageFooter>
</MessageContent>
</Message>
</div>
</template>Actions
Place message-level actions in MessageFooter, such as copy, retry, or feedback buttons.
<script setup lang="ts">
import { CopyIcon, RefreshCcwIcon, ThumbsDownIcon, ThumbsUpIcon } from '@lucide/vue'
import { Bubble, BubbleContent } from '@/components/ui/bubble'
import { Button } from '@/components/ui/button'
import { Message, MessageContent, MessageFooter } from '@/components/ui/message'
</script>
<template>
<div class="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 class="gap-2">
<span class="font-normal text-destructive">Failed to send</span>
<Button
variant="ghost"
size="icon-sm"
title="Retry"
aria-label="Retry"
>
<RefreshCcwIcon />
</Button>
</MessageFooter>
</MessageContent>
</Message>
</div>
</template>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
All Message parts render a <div> by default. Use as to choose another element, or as-child to merge the component's attributes and styles onto the single element or component in its default slot.
Message
The message row wrapper.
| Prop | Type | Default | Description |
|---|---|---|---|
align | "start" | "end" | "start" | The alignment of the message in the conversation. |
as | string | Component | "div" | The element or component to render. |
as-child | boolean | false | Render the default slot as the root and merge props onto it. |
class | string | - | Additional classes to apply to the row. |
MessageGroup
Groups consecutive messages from the same sender.
| Prop | Type | Default | Description |
|---|---|---|---|
as | string | Component | "div" | The element or component to render. |
as-child | boolean | false | Render the default slot as the root and merge props onto it. |
class | string | - | 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.
| Prop | Type | Default | Description |
|---|---|---|---|
as | string | Component | "div" | The element or component to render. |
as-child | boolean | false | Render the default slot as the root and merge props onto it. |
class | string | - | Additional classes to apply to the avatar wrapper. |
MessageContent
Wraps the header, message surface, and footer.
| Prop | Type | Default | Description |
|---|---|---|---|
as | string | Component | "div" | The element or component to render. |
as-child | boolean | false | Render the default slot as the root and merge props onto it. |
class | string | - | Additional classes to apply to the content wrapper. |
MessageHeader
Displays content above the message, such as a sender name. Aligns to the message side.
| Prop | Type | Default | Description |
|---|---|---|---|
as | string | Component | "div" | The element or component to render. |
as-child | boolean | false | Render the default slot as the root and merge props onto it. |
class | string | - | Additional classes to apply to the header. |
MessageFooter
Displays content below the message, such as status or actions. Aligns to the message side.
| Prop | Type | Default | Description |
|---|---|---|---|
as | string | Component | "div" | The element or component to render. |
as-child | boolean | false | Render the default slot as the root and merge props onto it. |
class | string | - | Additional classes to apply to the footer. |