ChatMessage

GitLab
Exiba uma mensagem de chat com ícone, avatar e ações.

Uso

O componente ChatMessage renderiza um elemento <article> para uma mensagem de chat de user ou assistant.

Hello! Tell me more about building AI chatbots with Nitro UI.
Use o componente ChatMessages para exibir uma lista de mensagens de chat.

Partes

Use a prop parts para exibir o conteúdo da mensagem usando o formato do AI SDK.

Hello! Tell me more about building AI chatbots with Nitro UI.
<template>
  <NChatMessage
    :parts="[
      {
        type: 'text',
        id: '1',
        text: 'Hello! Tell me more about building AI chatbots with Nitro UI.'
      }
    ]"
    role="user"
    id="1"
  />
</template>
A prop parts é o formato recomendado para o AI SDK. Cada part tem um type (ex.: 'text') e o conteúdo correspondente. O componente ChatMessage também suporta a prop content (obsoleta) para compatibilidade retroativa.

Lado

Use a prop side para exibir a mensagem à esquerda ou à direita.

Hello! Tell me more about building AI chatbots with Nitro UI.
<template>
  <NChatMessage
    side="right"
    :parts="[
      {
        type: 'text',
        id: '1',
        text: 'Hello! Tell me more about building AI chatbots with Nitro UI.'
      }
    ]"
    role="user"
    id="1"
  />
</template>
Ao usar o componente ChatMessages, a prop side é definida como left para mensagens de assistant e right para mensagens de user.

Variante

Use a prop variant para alterar o estilo da mensagem.

Hello! Tell me more about building AI chatbots with Nitro UI.
<template>
  <NChatMessage
    variant="soft"
    :parts="[
      {
        type: 'text',
        id: '1',
        text: 'Hello! Tell me more about building AI chatbots with Nitro UI.'
      }
    ]"
    role="user"
    id="1"
  />
</template>
Ao usar o componente ChatMessages, a prop variant é definida como naked para mensagens de assistant e soft para mensagens de user.

Cor 4.8+

Use a prop color para alterar a cor da mensagem.

Hello! Tell me more about building AI chatbots with Nitro UI.
<template>
  <NChatMessage
    variant="soft"
    color="primary"
    :parts="[
      {
        type: 'text',
        id: '1',
        text: 'Hello! Tell me more about building AI chatbots with Nitro UI.'
      }
    ]"
    role="user"
    id="1"
  />
</template>

Ícone

Use a prop icon para exibir um componente Icon ao lado da mensagem.

Hello! Tell me more about building AI chatbots with Nitro UI.
<template>
  <NChatMessage
    icon="i-lucide-user"
    variant="soft"
    side="right"
    :parts="[
      {
        type: 'text',
        id: '1',
        text: 'Hello! Tell me more about building AI chatbots with Nitro UI.'
      }
    ]"
    role="user"
    id="1"
  />
</template>

Avatar

Use a prop avatar para exibir um componente Avatar ao lado da mensagem.

Hello! Tell me more about building AI chatbots with Nitro UI.
<template>
  <NChatMessage
    :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 Nitro UI.'
      }
    ]"
    role="user"
    id="1"
  />
</template>

Você também pode usar a prop avatar.icon para exibir um ícone como avatar.

Nitro 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.
<template>
  <NChatMessage
    :avatar="{
      icon: 'i-lucide-bot'
    }"
    :parts="[
      {
        type: 'text',
        id: '1',
        text: 'Nitro 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>

Ações

Use a prop actions para exibir ações abaixo da mensagem, que serão mostradas ao passar o mouse sobre a mensagem.

Nitro 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.
<script setup lang="ts">
import type { ButtonProps } from '@nitro/ui'

const actions = ref<ButtonProps[]>([
  {
    label: 'Copy to clipboard',
    icon: 'i-lucide-copy'
  }
])
</script>

<template>
  <NChatMessage
    :actions="actions"
    :parts="[
      {
        type: 'text',
        id: '1',
        text: 'Nitro 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 '@nitro/ui'

const actions = ref<ButtonProps[]>([
  {
    label: 'Copy to clipboard',
    icon: 'i-lucide-copy'
  }
])
</script>

<template>
  <NChatMessage
    :actions="actions"
    :parts="[
      {
        type: 'text',
        id: '1',
        text: 'Nitro 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>

Exemplos

Confira a página de visão geral do Chat para instruções de instalação, configuração do servidor e exemplos de uso.

API

Props

Prop Default Type
as'article'any

The element or component this component should render as.

idstring

A unique identifier for the message.

role"system" | "user" | "assistant"

The role of the message.

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

iconany
avatar AvatarProps & { [key: string]: any; }
variant'naked' "solid" | "outline" | "soft" | "subtle" | "naked"
color'neutral' "primary" | "secondary" | "accent" | "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 label will be used in a tooltip. { size: 'xs', color: 'neutral', variant: 'ghost' }

compactfalseboolean

Render the message in a compact style. This is done automatically when used inside a UChatPalette.

content string
metadata TMetadata

The 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; }

Slots

Slot Type
headerUIMessage<TMetadata, TDataParts, TTools>
leadingUIMessage<TMetadata, TDataParts, TTools> & { avatar: (AvatarProps & { [key: string]: any; }) | undefined; ui: object; }
filesOmit<UIMessage<TMetadata, TDataParts, TTools>, "parts"> & { parts: FileUIPart[]; }
bodyUIMessage<TMetadata, TDataParts, TTools>
contentUIMessage<TMetadata, TDataParts, TTools> & { content?: string | undefined; }
actionsUIMessage<TMetadata, TDataParts, TTools> & { actions: (Omit<ButtonProps, "onClick"> & { onClick?: ((e: MouseEvent, message: UIMessage<TMetadata, TDataParts, TTools>) => void) | undefined; })[] | undefined; }

Tema

app.config.ts
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'
        ]
      },
      variants: {
        variant: {
          solid: '',
          outline: '',
          soft: '',
          subtle: '',
          naked: ''
        },
        color: {
          primary: '',
          secondary: '',
          accent: '',
          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'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nitro/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'
            ]
          },
          variants: {
            variant: {
              solid: '',
              outline: '',
              soft: '',
              subtle: '',
              naked: ''
            },
            color: {
              primary: '',
              secondary: '',
              accent: '',
              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'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitLab.

Changelog

No recent changes