Message
Menampilkan satu pesan dalam percakapan, lengkap dengan avatar, header, footer, dan perataan yang bisa diatur.
<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>Komponen Message menata satu pesan di dalam percakapan. Ia mengurus avatar, perataan, header, dan footer yang mengelilingi badan pesan.
Untuk aplikasi AI, komponen Message bisa Anda pakai menampilkan langkah penalaran, pemanggilan tool, dan balasan asisten.
Instalasi
CLI
npx shadcn-vue@latest add messageManual
Salin dan tempel kode sumber di GitHub ke project Anda.
Sesuaikan path import dengan struktur project Anda.
Penggunaan
<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>Catatan: Message yang mengurus tata letak barisnya — avatar, perataan, header, dan footer. Badan pesan yang terlihat ditampilkan di dalamnya memakai Bubble.
Komposisi
Susunan berikut adalah cara membangun sebuah message:
Message
├── MessageAvatar
└── MessageContent
├── MessageHeader
├── Bubble
└── MessageFooterPakai MessageGroup untuk menumpuk pesan berurutan dari pengirim yang sama:
MessageGroup
├── Message
└── MessageFitur
- Perataan awal dan akhir untuk baris pengirim dan penerima, lewat prop
align. - Slot avatar yang menempel di bagian bawah pesan tanpa bertabrakan dengan footer.
- Slot header dan footer untuk nama pengirim, status, dan aksi pesan.
- Footer mengikuti sisi pesan; aksinya tetap sejajar pada baris
align="end". - Pembungkus grup untuk menumpuk pesan berurutan dari pengirim yang sama.
- Pembungkus polimorfik lewat
asatauas-child. - Style tiap bagian bisa disesuaikan lewat prop
class.
Contoh
Avatar
Pakai MessageAvatar untuk menampilkan avatar di samping pesan. Isi align="end" pada pesan agar avatarnya berada di sisi akhir.
<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. |
Grup
Pakai MessageGroup untuk menumpuk pesan berurutan dari pengirim yang sama. Tampilkan MessageAvatar kosong pada pesan-pesan sebelumnya supaya sejajar dengan avatar di pesan terakhir.
<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 dan Footer
Pakai MessageHeader untuk nama pengirim, dan MessageFooter untuk metadata seperti status terkirim atau sudah dibaca.
<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>Aksi
Letakkan aksi tingkat pesan di dalam MessageFooter, misalnya tombol salin, coba lagi, atau umpan balik.
<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>Aksesibilitas
Message hanyalah pembungkus tata letak. Aksesibilitasnya datang dari konten yang Anda letakkan di dalamnya.
Beri label pada aksi yang hanya berupa ikon
Tombol aksi di MessageFooter biasanya hanya berupa ikon, jadi berilah masing-masing sebuah aria-label.
<MessageFooter>
<Button variant="ghost" size="icon" aria-label="Copy">
<CopyIcon />
</Button>
</MessageFooter>Pembaruan Status
Untuk pesan yang masih berjalan, pakai Marker dengan role="status" supaya teknologi bantu membacakan pembaruannya begitu muncul.
<Message>
<Marker role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent>Checking the logs...</MarkerContent>
</Marker>
</Message>Referensi API
Semua bagian Message menghasilkan <div> secara bawaan. Pakai as untuk memilih elemen lain, atau as-child untuk menggabungkan atribut dan style komponen ke satu elemen atau komponen di dalam slot bawaannya.
Message
Pembungkus baris pesan.
| 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
Mengelompokkan pesan berurutan dari pengirim yang sama.
| 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
Slot avatar yang menempel di bagian bawah pesan. Kalau pesannya punya MessageFooter, avatarnya bergeser naik agar tetap sejajar dengan badan pesan, bukan dengan 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
Membungkus header, badan pesan, dan 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
Menampilkan konten di atas pesan, misalnya nama pengirim. Mengikuti sisi pesannya.
| 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
Menampilkan konten di bawah pesan, misalnya status atau aksi. Mengikuti sisi pesannya.
| 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. |