# Message

Role-aware conversation messages with readable content and contextual actions.

- Package: `@mielui/svelte`
- Component version: `1.0.1`
- Depends on Mielui components: _internal/utils, typography
- Shared utilities: hugeicons-icon, utils.cn, utils.createContext

## Install

~~~~sh
pnpm dlx @mielui/svelte add message
~~~~

## API

This reference is generated at build time from the component manifest, public `index.ts`, and documentation examples below. Changes to those source files are reflected here in the published Markdown. Standard Svelte and HTML attributes accepted by the exported prop types are supported.


### Actions

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Avatar

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Body

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Content

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Metadata

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Name

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Root

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| labels | MessageLabels \| undefined | - | No | No | Overrides the built-in text and accessible names. Every key is optional and English is the fallback. |
| from | MessageFrom \| undefined | 'assistant' | No | No | Who sent the message. It sets the alignment and style. |
| status | MessageStatus \| undefined | 'idle' | No | No | Current state of the message, such as streaming or failed. |
| name | string \| undefined | - | No | No | Sender name. |
| timestamp | string \| undefined | - | No | No | Time shown with the message. |
| avatar | Snippet<[]> \| undefined | - | No | No | Avatar shown beside the message. |
| layout | Snippet<[]> \| undefined | - | No | No | Replaces the default arrangement of avatar, header, and content. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Status

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Time

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Public types

~~~~ts
import type { DefaultProps } from '@mielui/svelte/utils';
import type { Snippet } from 'svelte';
import type { HTMLAttributes, HTMLTimeAttributes } from 'svelte/elements';
import Root from './message.svelte';
import Actions from './message-actions.svelte';
import Avatar from './message-avatar.svelte';
import Body from './message-body.svelte';
import Content from './message-content.svelte';
import Metadata from './message-metadata.svelte';
import Name from './message-name.svelte';
import Status from './message-status.svelte';
import Time from './message-time.svelte';

export type MessageFrom = 'assistant' | 'user' | 'system';
export type MessageStatus = 'idle' | 'streaming' | 'error';

export type MessageLabels = {
    failed?: string;
    streaming?: string;
    complete?: string;
    actions?: string;
};

export type MessageRootProps = {
    /**
     * Overrides the built-in text and accessible names. Every key is optional and English is the
     * fallback.
     */
    labels?: MessageLabels;
    /** Who sent the message. It sets the alignment and style. */
    from?: MessageFrom;
    /** Current state of the message, such as streaming or failed. */
    status?: MessageStatus;
    /** Sender name. */
    name?: string;
    /** Time shown with the message. */
    timestamp?: string;
    /** Avatar shown beside the message. */
    avatar?: Snippet;
    /** Replaces the default arrangement of avatar, header, and content. */
    layout?: Snippet;
    /** Content rendered inside. */
    children?: Snippet;
} & DefaultProps &
    Omit<HTMLAttributes<HTMLElement>, 'children' | 'aria-busy'>;

export type MessageContentProps = DefaultProps & Omit<HTMLAttributes<HTMLDivElement>, 'children'>;

export type MessageActionsProps = DefaultProps &
    Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'role'>;

export type MessageAvatarProps = DefaultProps & HTMLAttributes<HTMLDivElement>;
export type MessageBodyProps = DefaultProps & HTMLAttributes<HTMLDivElement>;
export type MessageMetadataProps = DefaultProps & HTMLAttributes<HTMLElement>;
export type MessageNameProps = DefaultProps & HTMLAttributes<HTMLSpanElement>;
export type MessageTimeProps = DefaultProps & HTMLTimeAttributes;
export type MessageStatusProps = DefaultProps & HTMLAttributes<HTMLSpanElement>;
export { Actions, Avatar, Body, Content, Metadata, Name, Root, Status, Time };
~~~~

## Examples

### Hero

~~~~svelte
<script lang="ts">
    import { ThumbsUpIcon as ThumbsUp } from '@hugeicons/core-free-icons';
    import { Button } from '@mielui/svelte/components/button';
    import { CopyButton } from '@mielui/svelte/components/copy-button';
    import { Markdown } from '@mielui/svelte/components/markdown';
    import * as Message from '@mielui/svelte/components/message';
    import * as Reasoning from '@mielui/svelte/components/reasoning';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';

    const response = [
        '### Billing API v2',
        '',
        'The release separates invoice adjustments from payment collection and replaces `account_id` with `customer_id`.',
        '',
        '- Update webhook handlers for `invoice.adjusted`.',
        '- Backfill `customer_id` before switching reads.',
        '- Keep v1 retries active during the seven-day overlap.',
        '',
        '> Main migration risk: consumers that infer payment state from invoice updates.'
    ].join('\n');

    let helpful = $state(false);
    let status = $state('');

    function reportCopy() {
        status = 'Response copied to clipboard.';
    }

    function toggleHelpful() {
        helpful = !helpful;
    }
</script>

<div class="w-full max-w-3xl space-y-7">
    <Message.Root from="user">
        <Message.Content>
            Summarize what changed in our billing API and flag the migration work.
        </Message.Content>
    </Message.Root>

    <Message.Root from="assistant">
        <Message.Content class="space-y-3">
            <Reasoning.Root>
                <Reasoning.Trigger
                    title="Reviewed the v2 changelog and migration guide"
                    duration="2.6s"
                />
                <Reasoning.Content>
                    <p>Compared the event model, identifier changes, and compatibility window.</p>
                </Reasoning.Content>
            </Reasoning.Root>
            <Markdown content={response} />
        </Message.Content>
        <Message.Actions aria-label="Assistant response actions">
            <CopyButton
                text={response}
                labels={{ copy: 'Copy response', copied: 'Response copied' }}
                oncopy={reportCopy}
            />
            <Button
                variant="ghost"
                size="icon"
                aria-label={helpful ? 'Remove helpful rating' : 'Mark response as helpful'}
                aria-pressed={helpful}
                onclick={toggleHelpful}
            >
                <HugeiconsIcon
                    icon={ThumbsUp}
                    size={15}
                    fill={helpful ? 'currentColor' : 'none'}
                    aria-hidden="true"
                />
            </Button>
        </Message.Actions>
    </Message.Root>

    <p class="sr-only" role="status">{status}</p>
</div>
~~~~

### Role Variants

~~~~svelte
<script lang="ts">
    import * as Message from '@mielui/svelte/components/message';
</script>

<div class="w-full max-w-2xl space-y-7">
    <Message.Root from="assistant">
        <Message.Content>
            The deployment is healthy. Error rate and p95 latency are both below the rollback
            threshold.
        </Message.Content>
    </Message.Root>

    <Message.Root from="user">
        <Message.Content>Keep monitoring until the 30-minute mark.</Message.Content>
    </Message.Root>

    <Message.Root from="system">
        <Message.Content>
            Context compacted after 18 messages. Deployment events and decisions were preserved.
        </Message.Content>
    </Message.Root>
</div>
~~~~

### States

~~~~svelte
<script lang="ts">
    import { RotateLeft01Icon as RotateCcw } from '@hugeicons/core-free-icons';
    import { Button } from '@mielui/svelte/components/button';
    import { Markdown } from '@mielui/svelte/components/markdown';
    import type { MessageStatus } from '@mielui/svelte/components/message';
    import * as Message from '@mielui/svelte/components/message';
    import { ResponseStream } from '@mielui/svelte/components/response-stream';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';
    import { onDestroy } from 'svelte';

    const partialResponse =
        'All four regions are healthy. Error rates and response times remain within the release baseline.';

    let streaming = $state(true);
    let status = $state<MessageStatus>('error');
    let timer: ReturnType<typeof setTimeout> | undefined;

    function finishResponse() {
        streaming = false;
    }

    function retry() {
        status = 'streaming';
        timer = setTimeout(() => {
            status = 'idle';
        }, 1200);
    }

    onDestroy(() => {
        clearTimeout(timer);
    });
</script>

<div class="w-full max-w-2xl space-y-8">
    <Message.Root from="assistant" status={streaming ? 'streaming' : 'idle'}>
        <Message.Content>
            <ResponseStream textStream={partialResponse} speed={45} onComplete={finishResponse} />
        </Message.Content>
    </Message.Root>

    <Message.Root from="assistant" {status}>
        <Message.Content>
            {#if status === 'streaming'}
                <Markdown
                    content="Reconnecting to the warehouse and rebuilding the query"
                    streaming
                />
            {:else if status === 'idle'}
                <p>All four regions are healthy. The query completed after reconnecting.</p>
            {:else}
                <p>
                    The warehouse connection closed before the query completed. Retry when the
                    connection is available.
                </p>
            {/if}
        </Message.Content>
        {#if status === 'error'}
            <Message.Actions aria-label="Failed response actions">
                <Button variant="ghost" onclick={retry}>
                    <HugeiconsIcon icon={RotateCcw} size={14} aria-hidden="true" />
                    Retry response
                </Button>
            </Message.Actions>
        {/if}
    </Message.Root>
</div>
~~~~

For the rendered reference, visit [/docs/components/message](/docs/components/message).
