---
title: 'Ai.Message'
description: 'Shows a message from the user or the assistant, as a bubble or as plain content.'
version: 11.17.0
generatedAt: 2026-11-05T09:55:47.091Z
checksum: 2cff97061ffdf721070dd05d01ec5cd346abe10268fdd7a3033dce886230e741
---

# Ai.Message

**Experimental:** The AI extension and its component APIs may change in the future. Please give us feedback on your experience with them.

## Import

```tsx
import { Message } from '@dnb/eufemia/extensions/ai'
import '@dnb/eufemia/extensions/ai/style'
render(<Message />)
```

## Description

Bubble messages use theme colors and radius, small text and `0.5rem` by `1rem` padding. Avatars sit beside the bubble. Use the same avatar with the grouped prop on follow-up messages to retain alignment without repeating the icon; user avatars, names and timestamps are optional.

For a customer-service handover, use `from="assistant"`, an Avatar with the `customer_service` icon and omit `aiGenerated`. This represents the conversation side without labeling a person as AI.

`Ai.Message` shows a message from the user or the assistant, with an optional header (avatar, name, time and an AI-generated tag) and actions. User messages are placed on the right.

Compose the content inside `Ai.Message` using children: plain text, [Ai.Response](/web/patterns-and-extensions/ai/Response/) for markdown, [Ai.Task](/web/patterns-and-extensions/ai/Task/), or [Ai.Sources](/web/patterns-and-extensions/ai/Sources/). String children preserve line breaks. Set `from="user"` for user messages and give each message a unique DOM `id` to use the conversation scrolling and visibility hooks.

See [Integrations](/web/patterns-and-extensions/ai/integrations/) for connecting your chat data to these components.


## Demos

### Chat bubbles and sender groups


```tsx
render(<Conversation scrollBehavior="end" style={{
  height: '24rem',
  maxWidth: '23rem'
}}>
        <Message from="user">Chatmessage from the user.</Message>
        <Message aria-label="Aino" avatar={<Avatar size="small" icon={aino} hasLabel />}>
          Chatmessage from a bot.
          <br />
          Beep-bop.
        </Message>
        <Message grouped avatar={<Avatar size="small" icon={aino} hasLabel />}>
          <Response>More information from the same bot.</Response>
        </Message>
        <Message from="user">
          Thanks. This longer message wraps before reaching the opposite
          edge of the conversation.
        </Message>
        <Message avatar={<Avatar size="small" icon={customer_service} hasLabel />}>
          Hello from customer service.
        </Message>
      </Conversation>)
```


### Plain message

Content like a list can be shown without a bubble by using `variant="plain"`.


```tsx
render(<Message variant="plain" name="Solve" timestamp="15:24" avatar={<Avatar variant="secondary" hasLabel>
            S
          </Avatar>} aiGenerated>
        <Response>
          {'It seems you made **2 transfers** 12.02. Choose the correct one below.'}
        </Response>
      </Message>)
```


### Message actions


```tsx
const Feedback = () => {
  const [feedback, setFeedback] = useState<'positive' | 'negative' | null>(null);
  return <Message name="Aino" actions={<Actions role="group" aria-label="Answer feedback">
                  <CopyOnClick.Button size="small" iconSize="default" bounding copyContent="Your card is now blocked." title="Copy answer" tooltip="Copy answer" />
                  <Action icon={thumbs_up} label="Good answer" selected={feedback === 'positive'} onClick={() => setFeedback(feedback === 'positive' ? null : 'positive')} />
                  <Action icon={thumbs_down} label="Bad answer" selected={feedback === 'negative'} onClick={() => setFeedback(feedback === 'negative' ? null : 'negative')} />
                </Actions>}>
              Your card is now blocked.
            </Message>;
};
render(<Feedback />);
```


### Composing message content

Compose the content inside `Ai.Message` using `children`.


```tsx
<Message from="user">What did I spend on food?</Message>
<Message>
      <Task title="Checking your transactions" status="done" />
      <Response parseIncompleteMarkdown={false}>
        You spent **4 200 kr** on groceries in March.
      </Response>
      <Sources sources={[{
  url: 'https://www.dnb.no/privat/dagligbank',
  title: 'Everyday banking'
}]} />
    </Message>
```

<!-- eufemia-properties-usage:start -->
Some properties provide flexibility for specific integrations. Follow the component's usage guidance. Customizations beyond the documented patterns need a use case approved by DNB UX. [Read about intended use and customization](/web/styling-and-themes/configuration/component-properties/#intended-use-and-customization).
<!-- eufemia-properties-usage:end -->

## Properties


```json
{
  "props": {
    "from": {
      "doc": "Who wrote the message. User messages are placed on the right. Defaults to `assistant`.",
      "type": [
        "\"user\"",
        "\"assistant\""
      ],
      "status": "optional"
    },
    "id": {
      "doc": "Unique DOM id used by the conversation scrolling and visibility hooks.",
      "type": "string",
      "status": "optional"
    },
    "variant": {
      "doc": "Show the content in a bubble, or as plain content without background.",
      "type": [
        "\"bubble\"",
        "\"plain\""
      ],
      "defaultValue": "\"bubble\"",
      "status": "optional"
    },
    "name": {
      "doc": "Name shown in the header, e.g. `You` or the name of the assistant.",
      "type": "React.ReactNode",
      "status": "optional"
    },
    "timestamp": {
      "doc": "Time shown in the header after the name, e.g. `15:23`.",
      "type": "React.ReactNode",
      "status": "optional"
    },
    "avatar": {
      "doc": "Decorative avatar shown beside the bubble, e.g. an [Avatar](/uilib/components/avatar/) with `hasLabel`. Provide the sender name in the message or its accessible label; the avatar is hidden from screen readers.",
      "type": "React.ReactNode",
      "status": "optional"
    },
    "grouped": {
      "doc": "Continues the previous sender group. Retains avatar alignment while hiding the repeated avatar.",
      "type": "boolean",
      "status": "optional"
    },
    "aiGenerated": {
      "doc": "Show a tag telling that the content is generated by AI.",
      "type": "boolean",
      "status": "optional"
    },
    "actions": {
      "doc": "Content shown below the message, e.g. `Ai.Actions`.",
      "type": "React.ReactNode",
      "status": "optional"
    },
    "children": {
      "doc": "The content of the message.",
      "type": "React.ReactNode",
      "status": "optional"
    },
    "[Space](/uilib/layout/space/properties)": {
      "doc": "Spacing properties like `top` or `bottom` are supported.",
      "type": [
        "string",
        "object"
      ],
      "status": "optional"
    }
  },
  "usageGuidance": "Some properties provide flexibility for specific integrations. Follow the component's usage guidance. Customizations beyond the documented patterns need a use case approved by DNB UX. [Read about intended use and customization](/web/styling-and-themes/configuration/component-properties/#intended-use-and-customization)."
}
```


## Translations

| Key           | nb-NO       | en-GB        | sv-SE        | da-DK        |
| ------------- | ----------- | ------------ | ------------ | ------------ |
| `aiGenerated` | KI-generert | AI-generated | AI-genererat | AI-genereret |
