# Theming

The `ChatTheme` component is a wrapper that **you should use as the root** of all Compose UI Components. It's used to provide the default properties that help us style the application, such as:

- `isInDarkMode`: Flag that determines if the application should be themed in light or dark mode.
- `config`: A `ChatUiConfig` instance that holds feature flags grouped by area (translation, message list, composer, channel list, media gallery, attachment picker). See [ChatUiConfig](#chatuiconfig).
- `colors`: Defines a palette of colors we support in the app via `StreamDesign.Colors`. These are applied to all components and provide us with a dark/light mode by default, but can be used to override the design system completely.
- `typography`: Used for all text elements via `StreamDesign.Typography`, to apply different text styles to each component. Can be used to change the typography completely.
- `componentFactory`: Defines the component factory which renders the low-level
  composable components. Can be used to globally override specific components
  from the SDK screens or the high-level components.
  See: [Component Factory](/chat/docs/sdk/android/compose/general-customization/component-factory/).
- `attachmentPreviewHandlers`: Used to provide previews for all supported attachment types. If you do not wish to use the default previews, you can customize this.
- `reactionResolver`: Used to map reaction types to emoji characters. You can use the default resolver that supports 240+ emoji mappings, or provide a custom one. See [ReactionResolver](#reactionresolver).
- `messagePreviewIconFactory`: Used to create a preview icon for the given message type.
- `dateFormatter`: Used to define the timestamp formatting in the app. You can use the default formatting, or customize it to your needs.
- `durationFormatter`: Used to define the [DurationFormatter](#durationformatter) in the app. You can use the default formatting, or customize it to your needs.
- `channelNameFormatter`: Used to define the channel name formatting in the app. You can use the default implementation, or customize it according to your needs.
- `messagePreviewFormatter`: Used to define the message preview formatting in the app. You can use the default implementation, or customize the display of message previews according to your needs.
- `messageTextFormatter`: Used to format the message text displayed in the message list. See [MessageTextFormatter](#messagetextformatter).
- `searchResultNameFormatter`: Used to format names in search results.
- `imageLoaderFactory`: Used to create Coil image loader instances. You can use the default image loader factory, or provide a custom one.
- `messageAlignmentProvider`: Used to provide an alignment for a particular message. You can use the default implementation which aligns the messages of the current user to end, or customize it according to your needs.
- `streamCdnImageResizing`: Configuration for Stream CDN image resizing to optimize image loading.
- `streamMediaRecorder`: Used to record audio and video attachments.

<admonition type="note">

If any of these properties are not provided to our Compose UI Components due to not being wrapped inside of `ChatTheme`, you'll get an exception saying that the required properties are missing.

</admonition>

Let's see how to use the `ChatTheme` and how to customize the UI within.

## Using ChatTheme

To use the `ChatTheme`, simply wrap your UI content with it, like in the following example:

```kotlin {5,15}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ChatTheme {
            ChannelScreen(
                viewModelFactory = ChannelViewModelFactory(
                    context = this,
                    channelId = "messaging:123",
                ),
                onBackPressed = { finish() },
                onHeaderTitleClick = {}
            )
        }
    }
}
```

The `ChatTheme` provides default implementations for all its styling properties. That way, you can keep using our default color palette, typography, attachment factories and reaction types.

All you have to do is pass in the UI content you want to show, within its trailing lambda. This snippet above will produce the following UI. You'll also notice that if you switch to the dark theme in your system UI, the app will re-draw accordingly.

| Light theme                                                                                                          | Dark theme                                                                                                                          |
| -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| ![Default ChannelScreen component](@chat-sdk/android/v7-latest/_assets/compose_default_channel_screen_component.png) | ![Default Dark Mode ChannelScreen Component](@chat-sdk/android/v7-latest/_assets/compose_default_channel_screen_component_dark.png) |

Let's see how to customize the theme.

## Customization

To customize the `ChatTheme`, simply override any of the default properties by passing in your custom design style, like so:

```kotlin
setContent {
    ChatTheme(
        colors = StreamDesign.Colors.default().copy(
            accentPrimary = Color(0xFF6B5CE7),
            textPrimary = Color(0xFF1A1A2E),
        )
    ) {
        ChannelScreen(
            viewModelFactory = ChannelViewModelFactory(
                context = this,
                channelId = "messaging:123",
            ),
            onBackPressed = { finish() },
            onHeaderTitleClick = {}
        )
    }
}
```

In the snippet above, we customized the colors to use a different accent and text color. These colors propagate to all child components automatically.

Let's see what each property exposes and what the values are used for.

### StreamDesign.Colors

`StreamDesign.Colors` exposes all the color tokens used across the SDK. Use `StreamDesign.Colors.default()` for light mode or `StreamDesign.Colors.defaultDark()` for dark mode — `ChatTheme` automatically picks the right one based on `isInDarkMode`.

Customizing any of the tokens below propagates everywhere they're used. Bubble, button, badge, control, input, and reaction surfaces are derived internally from these public tokens, so changing a single semantic color (for example `accentPrimary`) updates every component that consumes it.

#### Color Scales

| Token    | Description                                                                                                                                 |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `brand`  | The brand accent color ramp. A `ColorScale` with stops `s50` through `s900`. Most accent and on-brand surfaces are derived from this scale. |
| `chrome` | The neutral gray ramp. A `ChromeScale` with stops `s0` through `s1000`. Most surface, text, and border tokens are derived from this scale.  |

#### Accent

| Token           | Description                                                                                                                                       |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `accentPrimary` | The main brand color. Used for interactive elements, buttons, links, and primary actions. Override this to apply your brand color across the SDK. |
| `accentError`   | Indicates a failure or destructive state. Used for failed messages, validation errors, and deletions.                                             |
| `accentSuccess` | Indicates a positive or completed state. Used for confirmations and success feedback.                                                             |
| `accentWarning` | Indicates a cautionary state. Used for warning messages and pending states.                                                                       |
| `accentNeutral` | A mid-tone gray for de-emphasized UI elements.                                                                                                    |

#### Text

| Token           | Description                                                                                                           |
| --------------- | --------------------------------------------------------------------------------------------------------------------- |
| `textPrimary`   | Main body text. Used for message content, titles, and any text that carries primary meaning.                          |
| `textSecondary` | Supporting metadata text. Used for timestamps, subtitles, and secondary labels.                                       |
| `textTertiary`  | De-emphasized text. Used for hints, placeholders, and lowest-priority supporting information.                         |
| `textDisabled`  | Text for non-interactive or unavailable states. Communicates that an element cannot be interacted with.               |
| `textOnAccent`  | Text on accent-colored surfaces. Stays white in both light and dark mode since the accent background does not invert. |
| `textOnInverse` | Text on inverse-colored surfaces. Flips between light and dark to maintain legibility when the background inverts.    |
| `textLink`      | Hyperlinks and inline actions. Uses the brand color to signal interactivity within text content.                      |

#### Background — Elevation

The elevation scale establishes a vertical hierarchy. Higher numbers sit visually closer to the user. In dark mode, values step progressively lighter as depth increases. In light mode, all levels are white.

![Elevation tokens in light and dark mode](@chat-sdk/android/v7-latest/_assets/elevation_tokens.png)

| Token                      | Description                                                                                   |
| -------------------------- | --------------------------------------------------------------------------------------------- |
| `backgroundCoreElevation0` | The base layer. Used as the reference point for the elevation scale.                          |
| `backgroundCoreElevation1` | Slightly raised surfaces. Used for content containers like the message list and channel list. |
| `backgroundCoreElevation2` | Floating and modal surfaces. Used for popovers, dropdowns, and dialogs.                       |
| `backgroundCoreElevation3` | Used for badge counts and reaction pills that float above other UI elements.                  |

#### Background — Surface

| Token                          | Description                                                                                                       |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `backgroundCoreApp`            | The outermost application background. Sits behind all surfaces.                                                   |
| `backgroundCoreSurfaceDefault` | Background for sectioned content areas. Used for grouped containers and distinct content regions.                 |
| `backgroundCoreSurfaceSubtle`  | A slightly receded background. Used for secondary containers or to create soft visual separation.                 |
| `backgroundCoreSurfaceCard`    | Background for contained, card-style elements. Matches the surface in light mode but lifts slightly in dark mode. |
| `backgroundCoreSurfaceStrong`  | A more prominent background. Used for elements that need to stand out from the main surface.                      |
| `backgroundCoreInverse`        | The opposite of the primary surface. Used for tooltips, snackbars, and high-contrast floating elements.           |
| `backgroundCoreOnAccent`       | Background for elements placed on an accent-colored surface. Ensures legibility against brand colors.             |
| `backgroundCoreHighlight`      | A tint for drawing attention to content. Used for highlights and pinned messages.                                 |
| `backgroundCoreOverlayLight`   | A light semi-transparent layer. Used to lighten surfaces and for hover states on dark backgrounds.                |
| `backgroundCoreOverlayDark`    | A dark semi-transparent layer. Used for image overlays.                                                           |
| `backgroundCoreScrim`          | A heavy semi-transparent layer. Used behind sheets, drawers, and modals to separate them from content.            |

#### Background — Utility

| Token                       | Description                                                                                                 |
| --------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `backgroundUtilitySelected` | Indicates an active or selected state. Used for selected messages, active list items, and toggled controls. |
| `backgroundUtilityDisabled` | Background for non-interactive elements. Flattens the element visually to signal unavailability.            |

#### Border — Core

| Token                     | Description                                                                                                               |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `borderCoreDefault`       | Standard border for surfaces and containers. Used for input fields, cards, and dividers on neutral backgrounds.           |
| `borderCoreSubtle`        | A lighter border for minimal separation. Used where a full-strength border would feel too heavy.                          |
| `borderCoreStrong`        | An emphatic border for elements that need clear definition. Used for focused containers and prominent dividers.           |
| `borderCoreOnInverse`     | Border on inverse-colored surfaces. Stays legible when the background flips between light and dark mode.                  |
| `borderCoreOnAccent`      | Border on accent-colored surfaces. Stays white in both light and dark mode since the accent background does not invert.   |
| `borderCoreOnSurface`     | Border for elements sitting on elevated surfaces.                                                                         |
| `borderCoreOpacitySubtle` | A very light transparent border. Used as a frame treatment on images and media attachments.                               |
| `borderCoreOpacityStrong` | A stronger transparent border for elements on colored or dark backgrounds. Used for waveform bars and similar treatments. |

#### Border — Utility

| Token                            | Description                                                                                                            |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `borderUtilitySelected`          | Border for selected or active states.                                                                                  |
| `borderUtilityFocused`           | Focus ring or focus border for keyboard navigation.                                                                    |
| `borderUtilityActive`            | Border for actively-pressed states.                                                                                    |
| `borderUtilityDisabled`          | Border for non-interactive elements. Matches the disabled surface to visually flatten the element.                     |
| `borderUtilityDisabledOnSurface` | Border for disabled elements on elevated surfaces. Stays visually distinct from the surface without drawing attention. |
| `borderUtilityError`             | Error state border.                                                                                                    |
| `borderUtilityWarning`           | Warning state border.                                                                                                  |
| `borderUtilitySuccess`           | Success state border.                                                                                                  |

#### Avatar

| Token                    | Description                                                                                                                |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `avatarBgPlaceholder`    | Background for the avatar placeholder shown while loading or for users without an image.                                   |
| `avatarTextPlaceholder`  | Text color used inside the avatar placeholder.                                                                             |
| `avatarPaletteBg1`–`5`   | Five background colors deterministically picked based on the user or channel ID, so each user gets a stable color.         |
| `avatarPaletteText1`–`5` | Text colors paired with the corresponding `avatarPaletteBg` slot — used for initials over the palette background.          |
| `avatarPresenceBorder`   | Outline color around the presence indicator dot. Designed to match the surface behind the avatar so the dot reads cleanly. |

#### Skeleton & System

| Token                      | Description                                                          |
| -------------------------- | -------------------------------------------------------------------- |
| `skeletonLoadingBase`      | Base color for the skeleton loading shimmer (placeholder surfaces).  |
| `skeletonLoadingHighlight` | Highlight color for the skeleton loading shimmer (the moving sweep). |
| `systemCaret`              | Color of the text input caret.                                       |

You can find the definitions of all 63 color properties in the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.kt).

### StreamDesign.Typography

`StreamDesign.Typography` defines the text styles used across the SDK:

| Property            | Usage                            |
| ------------------- | -------------------------------- |
| `bodyDefault`       | Standard body text               |
| `bodyEmphasis`      | Bold body text                   |
| `captionDefault`    | Small descriptive text           |
| `captionEmphasis`   | Bold small text                  |
| `headingExtraSmall` | Smallest heading                 |
| `headingSmall`      | Small heading                    |
| `headingMedium`     | Medium heading                   |
| `headingLarge`      | Large heading                    |
| `metadataDefault`   | Metadata text (timestamps, etc.) |
| `metadataEmphasis`  | Bold metadata text               |
| `numericMedium`     | Numeric displays (medium)        |
| `numericLarge`      | Numeric displays (large)         |
| `numericExtraLarge` | Numeric displays (extra large)   |

Use `StreamDesign.Typography.default()` for the default styles. You can pass a custom `FontFamily` to apply your own font:

```kotlin
ChatTheme(
    typography = StreamDesign.Typography.default(fontFamily = FontFamily(Font(R.font.my_custom_font)))
) {
    // Your UI content
}
```

### ChatUiConfig

`ChatUiConfig` consolidates feature flags that were previously individual parameters on `ChatTheme`. It groups configuration by feature area:

```kotlin
ChatTheme(
    config = ChatUiConfig(
        translation = TranslationConfig(
            enabled = true,               // Automatic message translation (enabled by default)
            showOriginalEnabled = true,    // Show option to see original text (enabled by default)
        ),
        messageList = MessageListConfig(
            videoThumbnailsEnabled = true,  // Show video thumbnails
        ),
        composer = ComposerConfig(
            audioRecordingEnabled = true,        // Voice message recording (enabled by default)
            audioRecordingSendOnComplete = false, // Attach recording for manual send (disabled by default)
            linkPreviewEnabled = false,          // Show link previews in composer
            floatingStyleEnabled = false,        // Use floating composer style
        ),
        channelList = ChannelListConfig(
            swipeActionsEnabled = true,          // Enable swipe-to-action on channels
            optionsVisibility = ChannelOptionsVisibility(
                // Toggle visibility of individual channel menu options
            ),
        ),
        mediaGallery = MediaGalleryConfig(
            isShareVisible = true,
            isGalleryVisible = true,
        ),
        attachmentPicker = AttachmentPickerConfig(
            useSystemPicker = true,              // Use native OS pickers
            modes = listOf(                      // Available picker modes
                GalleryPickerMode(),
                FilePickerMode(),
                CameraPickerMode(),
                PollPickerMode(),
                CommandPickerMode,
            ),
        ),
    ),
) {
    // Your UI content
}
```

### AttachmentPreviewHandler

`AttachmentPreviewHandler.defaultAttachmentHandlers()` provides default handlers for media, document and URL attachments.

If you want you can take a look at the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/handler/AttachmentPreviewHandler.kt).

You can customize file previews by creating your own list of `AttachmentPreviewHandler`s and overriding `ChatTheme.attachmentPreviewHandlers` with it.

### ReactionResolver

Used for defining reactions the user can add to messages. `ReactionResolver.defaultResolver()` provides 240+ emoji mappings with 5 default quick-access reactions (`like`, `love`, `haha`, `wow`, `sad`).

Reactions are emoji-based. To customize the available reactions:

```kotlin
val customResolver = DefaultReactionResolver(
    emojiMapping = linkedMapOf(
        "like" to "\uD83D\uDC4D",
        "love" to "\u2764\uFE0F",
        "fire" to "\uD83D\uDD25",
        "party" to "\uD83C\uDF89",
    ),
    defaultReactions = listOf("like", "love", "fire"),
)

ChatTheme(reactionResolver = customResolver) {
    // Your UI content
}
```

### DateFormatter

Used for formatting various times and dates such as the timestamp you see when a message is displayed. The default date formatter in `ChatTheme` is Stream's `DefaultDateFormatter`.

You can find out more about it by reading the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/DateFormatter.kt).

The date formatter can be customized by overriding `ChatTheme.dateFormatter` with your own implementation of `DateFormatter`.

```kotlin
ChatTheme(
    dateFormatter = object : DateFormatter {
        private val dateFormat: DateFormat = SimpleDateFormat("dd/MM/yyyy")
        private val timeFormat: DateFormat = SimpleDateFormat("HH:mm")

        override fun formatDate(date: Date?): String {
            date ?: return ""
            return dateFormat.format(date)
        }

        override fun formatTime(date: Date?): String {
            date ?: return ""
            return timeFormat.format(date)
        }

        override fun formatRelativeTime(date: Date?): String {
            date ?: return ""
            return DateUtils.getRelativeDateTimeString(
                applicationContext,
                date.time,
                DateUtils.MINUTE_IN_MILLIS,
                DateUtils.WEEK_IN_MILLIS,
                0
            ).toString()
        }
    }
) {
    // Your UI content
}
```

### TimeProvider

Used for providing the current time. The default time provider is `TimeProvider.DEFAULT`.

You can find out more about it by reading the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/TimeProvider.kt).

The time provider can be customized by overriding `ChatTheme.timeProvider` with your own implementation of `TimeProvider`.

```kotlin
ChatTheme(
    timeProvider = object : TimeProvider {
        override fun invoke(): Long {
            // Return the current time in milliseconds
            // System.currentTimeMillis() is the default implementation
            // You can use your own implementation here which can be synchronized with a server
            // to avoid local clock tampering
            return System.currentTimeMillis()
        }
    }
) {
    // Your UI content
}

```

### DurationFormatter

Used for formatting durations, such as the time elapsed since an audio playing was started.
The default duration formatter in `ChatTheme` formats durations in "hh:mm" format.

You can find out more about it by reading the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/DurationFormatter.kt).

The duration formatter can be customized by overriding `ChatTheme.durationFormatter` with your own implementation of `DurationFormatter`.

```kotlin
ChatTheme(
    durationFormatter = object : DurationFormatter {
        override fun format(durationInMillis: Int): String {
            // Your custom duration formatting logic
        }
    }
) {
    // Your UI content
}
```

### ChannelNameFormatter

Used for formatting channel names. The default channel name formatter is Stream's `DefaultChannelNameFormatter`.

You can find out more about it by reading the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/utils/ChannelNameFormatter.kt).

The channel name formatter is customizable by overriding `ChatTheme.channelNameFormatter` with your own instance of `ChannelNameFormatter`.

### MessagePreviewFormatter

Used for formatting the preview message in `ChannelItem`. The default message preview formatter is Stream's `DefaultMessagePreviewFormatter`.

You can find out more about it by reading the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessagePreviewFormatter.kt).

The message preview formatter can be customized by overriding `ChatTheme.messagePreviewFormatter` with your own implementation of `MessagePreviewFormatter`

```kotlin
ChatTheme(
    messagePreviewFormatter = object : MessagePreviewFormatter {
        override fun formatMessageTitle(message: Message): AnnotatedString {
            return buildAnnotatedString { append(message.user.name) }
        }
        override fun formatMessagePreview(
            message: Message,
            currentUser: User?,
            isDirectMessaging: Boolean,
        ): AnnotatedString {
            return buildAnnotatedString {
                append(message.text)
                // add your custom styling here
            }
        }
        override fun formatDraftMessagePreview(draftMessage: DraftMessage): AnnotatedString {
            return buildAnnotatedString { append(draftMessage.message.text) }
        }
    }
) {
    // Your UI content
}
```

### MessageTextFormatter

Used for formatting the message text in `MessageText` component. The default message text formatter is Stream's `DefaultMessageTextFormatter`.

You can find out more about it by reading the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageTextFormatter.kt).

The message text formatter can be customized by overriding `ChatTheme.messageTextFormatter` with your own implementation of `MessageTextFormatter`

```kotlin
ChatTheme(
    messageTextFormatter = object : MessageTextFormatter {
        override fun format(message: Message, currentUser: User?): AnnotatedString {
            return buildAnnotatedString {
                append(message.text)
                // Your custom styling here
            }
        }
    }
) {
    // Your UI content
}
```

### StreamCoilImageLoaderFactory

Provides the Coil `ImageLoader` used to load images, GIFs, and video thumbnails. The default is `StreamCoilImageLoaderFactory.defaultFactory()`. See [Customizing Image Loading](/chat/docs/sdk/android/compose/general-customization/image-loading/) for the registered Coil components, the canonical customization snippet, and troubleshooting for blank/placeholder images.

### MessageAlignmentProvider

Used for aligning messages. The default message alignment provider is `DefaultMessageAlignmentProvider` which will align your own messages to end while aligning the messages of others to start.

You can find out more about it by reading the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageAlignmentProvider.kt).

As with all of the other `ChatTheme` properties, you can easily customize how the messages are aligned by overriding `ChatTheme.messageAlignmentProvider` with your own implementation of `MessageAlignmentProvider`.

### StreamCdnImageResizing

Configures Stream CDN image resizing to optimize image loading and reduce bandwidth.

```kotlin
ChatTheme(
    streamCdnImageResizing = StreamCdnImageResizing.defaultStreamCdnImageResizing()
) {
    // Your UI content
}
```

## Accessing Theme Properties

All theme properties can be accessed anywhere within the `ChatTheme` scope using the `ChatTheme` object:

```kotlin
ChatTheme {
    val colors = ChatTheme.colors              // StreamDesign.Colors
    val typography = ChatTheme.typography       // StreamDesign.Typography
    val config = ChatTheme.config              // ChatUiConfig
    val factory = ChatTheme.componentFactory   // ChatComponentFactory

    // Use in your composables
    Text(
        text = "Hello",
        style = ChatTheme.typography.bodyDefault,
        color = ChatTheme.colors.textPrimary,
    )
}
```

The `ChatUiConfig` is also available via the `LocalChatUiConfig` composition local:

```kotlin
val config = LocalChatUiConfig.current
val isLinkPreviewEnabled = config.composer.linkPreviewEnabled
```


---

This page was last updated at 2026-05-17T10:18:20.696Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/android/compose/general-customization/chat-theme/](https://getstream.io/chat/docs/sdk/android/compose/general-customization/chat-theme/).