> For the complete documentation index, see [llms.txt](https://docs.ibexa.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ibexa.ai/developers/assistant/custom-message-types.md).

# Custom Message Types

In addition to the standard message types provided by the library, you can create custom message types to suit your application's specific needs. Custom message types allow you to define your own data structure and behavior for messages, enabling you to implement features that are not covered by the default message types.

## Built-in Message Types

Before diving into custom message types, it's important to understand the built-in message types that the library provides. These include:

* `CompoundMessageInterface`: Represents a message that combines several smaller messages e.g. Text + Image.
* `ImageMessageInterface`: Represents a message that contains an image.
* `TextMessageInterface`: Represents a simple text message.
* `StreamedTextMessageInterface`: Represents a text message that is streamed in parts.
* `VideoMessageInterface`: Represents a message that contains a video.
* `WelcomeMessageInterface`: Represents a welcome message that can be displayed when the assistant is first loaded.

You can use these built-in message types as they are, or you can extend them to create your own custom message types with additional properties and behaviors.

The most convenient way to re-use built-in message types is to use the `MessageFactory`. It provides methods for creating instances of the built-in message types.

```typescript
import { MessageFactory } from '@ibexa/ai-assistant-ui';

const foo = MessageFactory.createTextMessage('1', 'assistant', 'Hello, world!');
const bar = MessageFactory.createImageMessage('2', 'assistant', 'https://example.com/image.jpg');
```

## Message Interface

To create a custom message type, you need to define a message interface that extends the base `MessageInterface` provided by the `@ibexa/ai-assistant-ui` package. This interface should include any additional properties that are relevant to your custom message type.

```typescript
interface MessageInterface {
    id: string;
    serverId: string | null;
    type: string;
    role: 'assistant' | 'user';
    status: 'pending' | 'done' | 'failed';
}
```

Let's say you want to create a custom message type for displaying a map location. You can define a new interface that extends `MessageInterface` and includes additional properties for latitude and longitude.

```typescript
import { MessageInterface } from '@ibexa/ai-assistant-ui';

interface MapMessageInterface extends MessageInterface {
    type: 'map';
    latitude: number;
    longitude: number;
}
```

## Update Adapters

Introducing a custom message type also requires you to update your adapter to handle receiving messages of that type. The adapter is responsible for communicating with the backend, and it needs to be aware of the structure of your custom messages.

When implementing the adapter, ensure that it can parse incoming messages and create instances of your custom message type accordingly. This typically involves checking the message type and mapping the incoming data to the properties defined in your custom message interface.

## Message Renderer

Message renderers are functions that take a message object as an argument and return a `ReactNode` that represents how the message should be displayed in the UI.

```typescript
type MessageRenderer = (message: MessageInterface) => ReactNode;
```

You can create custom message renderers to define how your custom message types should be rendered.

```jsx
function MapMessageRenderer(message) {
    return (
        <MessageContainer message={message}>
            <MapLocation latitude={message.latitude} longitude={message.longitude} />
        </MessageContainer>
    );
}
```

`MessageContainer` is a component provided by the library that handles common UI elements for messages, such as displaying the avatar, timestamp, and message status. By wrapping your custom content in `MessageContainer`, you can ensure that it integrates seamlessly with the rest of the assistant's UI.

Considering the above example, you would need to implement the `MapLocation` component that takes latitude and longitude as props and renders a map accordingly.

{% hint style="info" %}
`MapLocation` component should be responsible only for rendering the actual message content, e.g. a map widget. Any additional UI elements such as avatar or timestamp are handled by the library and should not be included in the custom message renderer.
{% endhint %}

Message renderers must be passed to the Assistant component via the `messageComponents` prop as an object where keys are message types and values are corresponding renderers.

```jsx
const components = {
    map: MapMessageRenderer,
};

// ...

<Assistant adapter={adapter} messageComponents={components} />;
```

See the [Options](/developers/assistant/options.md#message-components) page for more details about the `messageComponents` option.

## Testing Custom Message Type

The easiest way to test your custom message type is to use `EchoAdapter` with predefined state. This allows you to simulate the behavior of your custom message type without needing to set up a full backend or real-time messaging system.

```jsx
import { Assistant, EchoAdapter } from '@ibexa/ai-assistant-ui';

function App() {
    const adapter = new EchoAdapter({
        data: [
            {
                id: '1',
                serverId: null,
                type: 'map',
                role: 'assistant',
                latitude: 40.7128,
                longitude: -74.0060,
                status: 'done',
            },

            // ...other messages
        ],
    });

    return <Assistant adapter={adapter} ... />;
}
```
