チャットメッセージ
使用法
ChatMessageコンポーネントは、userまたはassistantチャットメッセージ用の<article>要素をレンダリングします。
Parts
partsプロパティを使用して、AI SDK形式を使用してメッセージコンテンツを表示します。
<template>
<UChatMessage
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>partsプロパティは、AI SDKの推奨フォーマットです。各パートはtype例'text'と対応するコンテンツを持っています。ChatMessageコンポーネントは、後方互換性のため、非推奨のcontentプロパティもサポートしています。Side
sideプロパティを使用して、メッセージを左右に表示します。
<template>
<UChatMessage
side="right"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>Variant
メッセージのスタイルを変更するにはvariantプロパティを使用します。
<template>
<UChatMessage
variant="soft"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>Color badge{label="4.8+" class="align-text-top"}
colorプロパティを使用してメッセージの色を変更します。
<template>
<UChatMessage
variant="soft"
color="primary"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>Icon
iconプロパティを使用して、メッセージの横にIconコンポーネントを表示します。
<template>
<UChatMessage
icon="i-lucide-user"
variant="soft"
side="right"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>アバター
avatarプロパティを使用して、メッセージの横にAvatarコンポーネントを表示します。
<template>
<UChatMessage
:avatar="{
src: 'https://github.com/benjamincanac.png',
loading: 'lazy'
}"
variant="soft"
side="right"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>avatar.iconプロパティを使用してアイコンをアバターとして表示することもできます。
<template>
<UChatMessage
:avatar="{
icon: 'i-lucide-bot'
}"
:parts="[
{
type: 'text',
id: '1',
text: 'Nuxt UI offers several features for building AI chatbots including the ChatMessage, ChatMessages, and ChatPrompt components. Best practices include using the Chat class from AI SDK, implementing proper message styling with variants, and utilizing the built-in actions for message interactions. The components are fully customizable with theming support and responsive design.'
}
]"
role="assistant"
id="1"
/>
</template>Actions
actionsプロパティを使用して、メッセージの上にマウスオーバーしたときに表示されるアクションをメッセージの下に表示します。
<script setup lang="ts">
import type { ButtonProps } from '@nuxt/ui'
const actions = ref<ButtonProps[]>([
{
label: 'Copy to clipboard',
icon: 'i-lucide-copy'
}
])
</script>
<template>
<UChatMessage
:actions="actions"
:parts="[
{
type: 'text',
id: '1',
text: 'Nuxt UI offers several features for building AI chatbots including the ChatMessage, ChatMessages, and ChatPrompt components. Best practices include using the Chat class from AI SDK, implementing proper message styling with variants, and utilizing the built-in actions for message interactions. The components are fully customizable with theming support and responsive design.'
}
]"
role="user"
id="1"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ButtonProps } from '@nuxt/ui'
const actions = ref<ButtonProps[]>([
{
label: 'Copy to clipboard',
icon: 'i-lucide-copy'
}
])
</script>
<template>
<UChatMessage
:actions="actions"
:parts="[
{
type: 'text',
id: '1',
text: 'Nuxt UI offers several features for building AI chatbots including the ChatMessage, ChatMessages, and ChatPrompt components. Best practices include using the Chat class from AI SDK, implementing proper message styling with variants, and utilizing the built-in actions for message interactions. The components are fully customizable with theming support and responsive design.'
}
]"
role="user"
id="1"
/>
</template>サンプル
API
Props
| Prop | Default | Type |
|---|---|---|
as | 'article' | anyThe element or component this component should render as. |
id | stringA unique identifier for the message. | |
role | "system" | "user" | "assistant"The role of the message. | |
parts | UIMessagePart<TDataParts, TTools>[]The parts of the message. Use this for rendering the message in the UI. System messages should be avoided (set the system prompt on the server instead). They can have text parts. User messages can have text parts and file parts. Assistant messages can have text, reasoning, tool invocation, and file parts. | |
icon | any | |
avatar | AvatarProps & { [key: string]: any; } | |
variant | 'naked' | "solid" | "outline" | "soft" | "subtle" | "naked" |
color | 'neutral' | "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral" |
side | 'left' | "left" | "right" |
actions | (Omit<ButtonProps, "onClick"> & { onClick?: ((e: MouseEvent, message: UIMessage<TMetadata, TDataParts, TTools>) => void) | undefined; })[]Display a list of actions under the message.
The | |
compact | false | boolean Render the message in a compact style.
This is done automatically when used inside a |
content | string | |
metadata | TMetadataThe metadata of the message. | |
ui | { root?: SlotClass; header?: SlotClass; container?: SlotClass; body?: SlotClass; leading?: SlotClass; leadingIcon?: SlotClass; leadingAvatar?: SlotClass; leadingAvatarSize?: SlotClass; files?: SlotClass; content?: SlotClass; actions?: SlotClass; } |
スロット
| Slot | Type |
|---|---|
header | UIMessage<TMetadata, TDataParts, TTools> |
leading | UIMessage<TMetadata, TDataParts, TTools> & { avatar: (AvatarProps & { [key: string]: any; }) | undefined; ui: object; } |
files | Omit<UIMessage<TMetadata, TDataParts, TTools>, "parts"> & { parts: FileUIPart[]; } |
body | UIMessage<TMetadata, TDataParts, TTools> |
content | UIMessage<TMetadata, TDataParts, TTools> & { content?: string | undefined; } |
actions | UIMessage<TMetadata, TDataParts, TTools> & { actions: (Omit<ButtonProps, "onClick"> & { onClick?: ((e: MouseEvent, message: UIMessage<TMetadata, TDataParts, TTools>) => void) | undefined; })[] | undefined; } |
Theme
export default defineAppConfig({
ui: {
chatMessage: {
slots: {
root: 'group/message relative w-full',
header: 'flex mb-1.5',
container: 'relative flex items-start',
body: 'min-w-0',
leading: 'inline-flex items-center justify-center min-h-6',
leadingIcon: 'shrink-0',
leadingAvatar: 'shrink-0',
leadingAvatarSize: '',
files: 'flex items-center gap-1.5',
content: 'relative text-pretty wrap-break-word *:first:mt-0 *:last:mb-0',
actions: [
'[@media(hover:hover)]:opacity-0 group-hover/message:opacity-100 absolute bottom-0 flex items-center',
'transition-opacity ease-out'
]
},
variants: {
variant: {
solid: '',
outline: '',
soft: '',
subtle: '',
naked: ''
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
side: {
left: {},
right: {
container: 'justify-end ms-auto max-w-[75%]',
header: 'justify-end',
actions: 'right-0'
}
},
leading: {
true: ''
},
actions: {
true: ''
},
compact: {
true: {
root: 'scroll-mt-3',
container: 'gap-1.5 pb-3',
content: 'space-y-2',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs'
},
false: {
root: 'scroll-mt-4 sm:scroll-mt-6',
container: 'gap-3 pb-8',
content: 'space-y-4',
leadingIcon: 'size-8',
leadingAvatarSize: 'md'
}
}
},
compoundVariants: [
{
compact: true,
actions: true,
class: {
container: 'pb-8'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: false,
class: {
content: 'px-4 py-3 rounded-lg min-h-12',
leading: 'mt-2'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: true,
class: {
content: 'px-2 py-1 rounded-lg min-h-8',
leading: 'mt-1'
}
},
{
variant: 'naked',
side: 'left',
class: {
body: 'w-full',
content: 'w-full'
}
},
{
color: 'primary',
variant: 'solid',
class: {
content: 'bg-primary text-inverted'
}
},
{
color: 'primary',
variant: 'outline',
class: {
content: 'text-primary ring ring-primary/25'
}
},
{
color: 'primary',
variant: 'soft',
class: {
content: 'bg-primary/10 text-primary'
}
},
{
color: 'primary',
variant: 'subtle',
class: {
content: 'bg-primary/10 text-primary ring ring-primary/25'
}
},
{
color: 'primary',
variant: 'naked',
class: {
content: 'text-primary'
}
},
{
color: 'neutral',
variant: 'solid',
class: {
content: 'bg-inverted text-inverted'
}
},
{
color: 'neutral',
variant: 'outline',
class: {
content: 'bg-default ring ring-default'
}
},
{
color: 'neutral',
variant: 'soft',
class: {
content: 'bg-elevated/50'
}
},
{
color: 'neutral',
variant: 'subtle',
class: {
content: 'bg-elevated/50 ring ring-default'
}
}
],
defaultVariants: {
side: 'left',
variant: 'naked',
color: 'neutral'
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
chatMessage: {
slots: {
root: 'group/message relative w-full',
header: 'flex mb-1.5',
container: 'relative flex items-start',
body: 'min-w-0',
leading: 'inline-flex items-center justify-center min-h-6',
leadingIcon: 'shrink-0',
leadingAvatar: 'shrink-0',
leadingAvatarSize: '',
files: 'flex items-center gap-1.5',
content: 'relative text-pretty wrap-break-word *:first:mt-0 *:last:mb-0',
actions: [
'[@media(hover:hover)]:opacity-0 group-hover/message:opacity-100 absolute bottom-0 flex items-center',
'transition-opacity ease-out'
]
},
variants: {
variant: {
solid: '',
outline: '',
soft: '',
subtle: '',
naked: ''
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
side: {
left: {},
right: {
container: 'justify-end ms-auto max-w-[75%]',
header: 'justify-end',
actions: 'right-0'
}
},
leading: {
true: ''
},
actions: {
true: ''
},
compact: {
true: {
root: 'scroll-mt-3',
container: 'gap-1.5 pb-3',
content: 'space-y-2',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs'
},
false: {
root: 'scroll-mt-4 sm:scroll-mt-6',
container: 'gap-3 pb-8',
content: 'space-y-4',
leadingIcon: 'size-8',
leadingAvatarSize: 'md'
}
}
},
compoundVariants: [
{
compact: true,
actions: true,
class: {
container: 'pb-8'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: false,
class: {
content: 'px-4 py-3 rounded-lg min-h-12',
leading: 'mt-2'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: true,
class: {
content: 'px-2 py-1 rounded-lg min-h-8',
leading: 'mt-1'
}
},
{
variant: 'naked',
side: 'left',
class: {
body: 'w-full',
content: 'w-full'
}
},
{
color: 'primary',
variant: 'solid',
class: {
content: 'bg-primary text-inverted'
}
},
{
color: 'primary',
variant: 'outline',
class: {
content: 'text-primary ring ring-primary/25'
}
},
{
color: 'primary',
variant: 'soft',
class: {
content: 'bg-primary/10 text-primary'
}
},
{
color: 'primary',
variant: 'subtle',
class: {
content: 'bg-primary/10 text-primary ring ring-primary/25'
}
},
{
color: 'primary',
variant: 'naked',
class: {
content: 'text-primary'
}
},
{
color: 'neutral',
variant: 'solid',
class: {
content: 'bg-inverted text-inverted'
}
},
{
color: 'neutral',
variant: 'outline',
class: {
content: 'bg-default ring ring-default'
}
},
{
color: 'neutral',
variant: 'soft',
class: {
content: 'bg-elevated/50'
}
},
{
color: 'neutral',
variant: 'subtle',
class: {
content: 'bg-elevated/50 ring ring-default'
}
}
],
defaultVariants: {
side: 'left',
variant: 'naked',
color: 'neutral'
}
}
}
})
]
})