> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-android-llms-indexes.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Message Bubble Factory

> Customize message bubble structure, layout, and behavior with BubbleFactory.

<Note>
  **Renamed in v6.** v5's `MessageTemplate` API (`CometChatMessageTemplate`, `setTemplates`,
  `setType`, `setCategory`, `setBubbleView`, `setMessageReceipt`) **does not ship** in
  `com.cometchat:chatuikit-kotlin-android:6.0.5` — verified against the published artifact. Bubble
  customization is now `BubbleFactory`. If you are migrating, see
  [Upgrading from v5](/ui-kit/android/upgrading-from-v5).
</Note>

A **`BubbleFactory`** decides how one message category/type is rendered inside the
[MessageList](/ui-kit/android/message-list). You subclass it, say which messages it handles, and
supply the view. Everything you do not override keeps the kit's default rendering.

## When to Use This

* Render a **custom message type** (a contact card, an order receipt, a poll)
* Replace how a **built-in** type renders (text, image, video)
* Replace the **whole bubble** rather than just its content area
* Add an avatar / header / footer / thread view to a bubble

## Prerequisites

* CometChat Android UI Kit dependency added
* `CometChatUIKit.initFromSettings()` completed and a user logged in
* A `CometChatMessageList` on screen

## Quick Start

<Tabs>
  <Tab title="Kotlin (XML Views)">
    ```kotlin ContactBubbleFactory.kt theme={null}
    import android.content.Context
    import android.view.View
    import androidx.recyclerview.widget.RecyclerView
    import com.cometchat.chat.constants.CometChatConstants
    import com.cometchat.chat.models.BaseMessage
    import com.cometchat.uikit.core.constants.UIKitConstants
    import com.cometchat.uikit.kotlin.presentation.shared.messagebubble.BubbleFactory

    class ContactBubbleFactory : BubbleFactory() {

        // WHICH messages this factory handles.
        override fun getCategory(): String = CometChatConstants.CATEGORY_CUSTOM
        override fun getType(): String = "contact"

        // Called ONCE per recycled view. The message is NOT available yet — only the factory key.
        override fun createContentView(context: Context): View = ContactCardView(context)

        // Called for EVERY message shown in that view. Bind your data here.
        override fun bindContentView(
            view: View,
            message: BaseMessage,
            alignment: UIKitConstants.MessageBubbleAlignment,
            holder: RecyclerView.ViewHolder?,
            position: Int
        ) {
            (view as ContactCardView).bind(message)
        }
    }
    ```

    Register it on the list:

    ```kotlin MessageActivity.kt theme={null}
    messageList.setBubbleFactories(listOf(ContactBubbleFactory()))
    ```
  </Tab>

  <Tab title="Jetpack Compose">
    ```kotlin ContactBubbleFactory.kt theme={null}
    import androidx.compose.runtime.Composable
    import com.cometchat.chat.constants.CometChatConstants
    import com.cometchat.chat.models.BaseMessage
    import com.cometchat.uikit.core.constants.UIKitConstants
    import com.cometchat.uikit.compose.presentation.shared.messagebubble.BubbleFactory
    import com.cometchat.uikit.compose.presentation.shared.messagebubble.CometChatMessageBubbleStyle
    import com.cometchat.uikit.compose.shared.formatters.CometChatTextFormatter

    class ContactBubbleFactory : BubbleFactory {

        override fun getCategory(): String = CometChatConstants.CATEGORY_CUSTOM
        override fun getType(): String = "contact"

        // Compose returns a composable lambda — there is no create/bind split.
        override fun getContentView(
            message: BaseMessage,
            alignment: UIKitConstants.MessageBubbleAlignment,
            style: CometChatMessageBubbleStyle,
            textFormatters: List<CometChatTextFormatter>
        ): @Composable () -> Unit = {
            ContactCard(message = message)
        }
    }
    ```

    Pass it to the list:

    ```kotlin theme={null}
    CometChatMessageList(
        user = user,
        bubbleFactories = listOf(ContactBubbleFactory())
    )
    ```
  </Tab>
</Tabs>

## Core Concepts

### The factory key is `category` + `type`

`getCategory()` and `getType()` are how the list decides which factory renders a message. Use the
SDK constants for built-ins (`CometChatConstants.CATEGORY_MESSAGE` with `MESSAGE_TYPE_TEXT`,
`MESSAGE_TYPE_IMAGE`, …) and `CATEGORY_CUSTOM` with your own type string for custom messages. A
message with no matching factory falls back to the kit's own rendering.

### create/bind is a recycling contract (Views)

`createContentView()` runs **once** per recycled view and the message is deliberately not available
there — only the factory key. `bindContentView()` runs **every time** a message is displayed in that
view. Building views in `bind` (or caching per-message state in `create`) is the usual cause of
bubbles showing the wrong message after scrolling.

Compose has no such split: `getContentView()` receives the message and returns a composable.

### Content view vs whole bubble

| You override                                                                 | You get                                                                               |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `createContentView` / `bindContentView` (Views) · `getContentView` (Compose) | the bubble's content area, inside the kit's bubble chrome                             |
| `createBubbleView` / `bindBubbleView` (Views) · `getBubbleView` (Compose)    | the **entire** bubble — the kit draws no chrome, and the content hooks are not called |

## Optional view slots

All are optional; return `null` (Views) or leave the default (Compose) to keep the kit's own.

| Slot        | Views                                         | Purpose                                |
| ----------- | --------------------------------------------- | -------------------------------------- |
| Leading     | `createLeadingView` / `bindLeadingView`       | typically the avatar                   |
| Header      | `createHeaderView` / `bindHeaderView`         | above the bubble (e.g. sender name)    |
| Reply       | `createReplyView` / `bindReplyView`           | the quoted-reply preview               |
| Bottom      | `createBottomView` / `bindBottomView`         | below the content (e.g. reactions row) |
| Status info | `createStatusInfoView` / `bindStatusInfoView` | timestamp + receipts                   |
| Thread      | `createThreadView` / `bindThreadView`         | the thread-replies indicator           |
| Style       | `getBubbleStyle(...)`                         | per-message bubble style override      |

## Creating a new custom message type

1. Send the message with the SDK using `CometChatConstants.CATEGORY_CUSTOM` and your own type
   string (see [Custom Messages](/sdk/android/v5/send-message)).
2. Write a `BubbleFactory` whose `getCategory()`/`getType()` match exactly.
3. Register it with `setBubbleFactories(...)` (Views) or the `bubbleFactories` parameter (Compose).

Messages of a type with no registered factory render with the kit's default custom-message bubble,
so register the factory on every screen that shows those messages.

## Next Steps

* [Message List](/ui-kit/android/message-list) — where factories are registered
* [Message Bubble Styling](/ui-kit/android/component-styling) — styling without replacing views
* [Upgrading from v5](/ui-kit/android/upgrading-from-v5) — the `MessageTemplate` → `BubbleFactory` move
