---
title: AI Elements
description: Conversation and message primitives for AI chat surfaces.
category: AiReasoning
status: beta
since: 0.3.9
tags: [ai, conversation, message]
syncStatus: reviewed
verified: true
---

## Usage

### Basic
`showAvatar` adds an avatar column; without `avatar`, it shows the name's initial.
::::TuffDemoWrapper{demo="AiElementsAiConversationDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import type { AiElementMessage } from '@talex-touch/tuffex/ai-elements'

  const messages: AiElementMessage[] = [
    { id: 'u1', role: 'user', content: 'Summarize this release review.', name: 'You' },
    { id: 'a1', role: 'assistant', content: 'Reviewed **component docs**.', name: 'Tuff AI' },
    { id: 't1', role: 'tool', content: '', name: 'Verifier', status: 'streaming' },
  ]
  </script>

  <template>
    <TxAiConversation :messages="messages" show-avatar />
  </template>
---
::::

### Best Practices

- Keep provider-specific streaming state in the host and map it into `status`.
- Use stable ids from the conversation store, not array indexes.
- Keep `markdown` on for assistant output; sanitize untrusted Markdown at the data boundary.
- Put tool cards and attachments in the `default` slot instead of custom HTML in `content`.

## API Reference

### TxAiConversation

#### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `messages` | `AiElementMessage[]` | required | Transcript data, rendered in order. |
| `markdown` | `boolean` | `true` | Renders bodies through `TxMarkdownView`. |
| `compact` | `boolean` | `false` | Tightens message spacing. |
| `emptyText` | `string` | `'No messages yet'` | Empty-state copy when no message is renderable. |
| `showAvatar` | `boolean` | `false` | Shows an avatar column for every message. |

#### Slots

| Slot | Props | Description |
|------|------|-------------|
| `empty` | - | Replaces the built-in empty state. |
| `default` / `avatar` / `markdown-renderer` / `tool-result` | Same as `TxAiMessage` | Forwarded to every `TxAiMessage`. |

### TxAiMessage

#### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `message` | `AiElementMessage` | required | The message to render. |
| `markdown` | `boolean` | `true` | Renders the body through `TxMarkdownView`; `false` keeps plain text with its line breaks. |
| `compact` | `boolean` | `false` | Compact row density and a smaller avatar. |
| `showAvatar` | `boolean` | `false` | Shows the avatar. |
| `typingLabel` | `string` | `'AI is typing'` | Accessible name of the typing indicator; `TxAiConversation` doesn't forward it. |

#### Events

| Event | Params | Description |
|------|--------|-------------|
| `open-source` | `(source: AiSourceItem)` | Fires when a source in a `sources` part is clicked; navigation is prevented. `TxAiConversation` doesn't forward it. |

#### Slots

| Slot | Props | Description |
|------|------|-------------|
| `default` | `{ message }` | Replaces the content region; for tool cards, attachments, or custom renderers. |
| `avatar` | `{ message }` | Replaces the avatar when `showAvatar` is on. |
| `markdown-renderer` | `{ part, message, streaming }` | Replaces a `text` part's rendering; `streaming` is true only for the last text part of a streaming message. |
| `tool-result` | `{ part }` | Replaces the result area of a `tool-call` part once it is `done`. |

### Types

#### AiElementMessage

| Field | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `string` | required | Stable key. |
| `role` | `'user' \| 'assistant' \| 'system' \| 'tool'` | required | Visual and semantic role. |
| `content` | `string` | required | Text or Markdown source. |
| `createdAt` | `number \| string \| Date` | - | The host's timestamp. |
| `name` | `string` | - | Display name; falls back to You / AI / Tool / System. |
| `avatar` | `string` | - | Avatar image URL. |
| `status` | `'pending' \| 'streaming' \| 'complete' \| 'error'` | - | Message lifecycle state. |
| `parts` | `AiMessagePart[]` | - | When non-empty, these parts render in order instead of `content`. |

#### AiMessagePart

:::TuffCodeBlock{lang="ts"}
---
code: |
  type AiMessagePart =
    | { type: 'text', text: string } // markdown-renderer slot or TxMarkdownView
    | { type: 'reasoning', text: string, done?: boolean, durationMs?: number } // TxReasoningDisclosure
    | AiToolCallPart // type: 'tool-call', rendered by TxToolCallCard
    | { type: 'attachment', attachments: AiAttachment[] } // read-only TxAttachmentTray
    | { type: 'sources', sources: AiSourceItem[] } // TxSources
---
:::

## Overview

- A message with empty `content` is not rendered unless it has `parts` or its `status` is `pending` or `streaming`.
- Such an empty message without `parts` shows a typing indicator.
- `TxAiConversation` is an `aria-live="polite"` region: appended messages are announced without stealing focus.

## Technologies

- Source: `packages/tuffex/packages/components/src/ai-elements/`.

<TuffDocSourceLink />
