# Custom Channel Header

In this cookbook recipe, you'll learn how to customize the channel header to match your app's design. The header is a key part of the chat experience, displaying channel information and navigation controls.

## Overview

The channel header sits above the list of messages and shows the channel name and avatar, members and online members count, current connection status, and a back button. While the SDK provides a default header that works well for most cases, you may want to customize it to:

- Match your app's visual design and branding
- Display additional channel information
- Add custom actions or buttons

In this example, we'll customize the `ChannelHeader` component to look similar to WhatsApp's conversation title bar.

![Custom Channel Header](/data/docs/chat-sdk/android/v7-latest/_assets/cookbook_custom_channel_header.png)

## State Handling

Let's define a new composable, `CustomChannelHeader`, that takes the channel `cid` and a back handler as parameters.

```kotlin
@Composable
fun CustomChannelHeader(cid: String?, onBackClick: () -> Unit = {})
```

Inside it we'll use the built-in `MessageListViewModel` to acquire the state that we need.

```kotlin
val viewModel = viewModel(
    modelClass = MessageListViewModel::class.java,
    factory = ChannelViewModelFactory(LocalContext.current, channelId = cid)
)

val channel = viewModel.channel
val connectionState by viewModel.connectionState.collectAsStateWithLifecycle()
val currentUser by viewModel.user.collectAsStateWithLifecycle()
```

We pass `channel`, `connectionState` and `currentUser` to the `ChannelHeader` component, alongside other state that we get from the view model:

```kotlin
ChannelHeader(
    channel = channel,
    currentUser = currentUser,
    connectionState = connectionState,
    typingUsers = viewModel.typingUsers,
    messageMode = viewModel.messageMode,
    onBackPressed = onBackClick,
)
```

## Leading Content

Let's customize the leading content, which represents the start slot of the header. This is a very simple customization: we just replace the default back arrow with our custom one.

Note that we'll also use `CustomHeaderButton` for other buttons that we'll add later to the header.

```kotlin
ChannelHeader(
    // State
    leadingContent = { CustomHeaderLeadingContent(onClick = onBackClick) },
)

@Composable
private fun CustomHeaderLeadingContent(onClick: () -> Unit) {
    CustomHeaderButton(
        iconRes = R.drawable.ic_back,
        contentDescription = "Back",
        onClick = onClick
    )
}

@Composable
private fun CustomHeaderButton(
    @DrawableRes iconRes: Int,
    contentDescription: String,
    onClick: () -> Unit,
) {
    IconButton(
        onClick = onClick,
        content = {
            Icon(
                painter = painterResource(id = iconRes),
                contentDescription = contentDescription,
                tint = Color.White,
            )
        }
    )
}
```

## Center Content

Now, let's use the middle slot of the header, named `centerContent`, and pass our `CustomHeaderCenterContent` to it.

We use several composables and utility methods in order to display the channel avatar, name, member and online member count:

- The `ChannelAvatar` component from our SDK to show the avatar. Based on the state of the channel and the number of members, it shows different types of images.
- The `ChatTheme.ChannelNameFormatter.formatChannelName` method to show the name of the channel, based on a set of rules. Search for the `DefaultChannelNameFormatter` component for more info.
- The `channel.getMembersStatusText` extension method to show either a member count for a group channel or the _last seen_ text for a direct one-to-one conversation.

```kotlin
ChannelHeader(
    // State
    leadingContent = { CustomHeaderLeadingContent(onClick = onBackClick) },
    centerContent = { CustomHeaderCenterContent(channel = channel, currentUser = currentUser) },
)

@Composable
private fun CustomHeaderCenterContent(channel: Channel, currentUser: User?) {
    Row {
        ChannelAvatar(
            modifier = Modifier.size(40.dp),
            channel = channel,
            currentUser = currentUser,
        )
        Spacer(modifier = Modifier.size(10.dp))
        Column {
            Text(
                text = ChatTheme.channelNameFormatter.formatChannelName(channel, currentUser),
                color = Color.White,
                fontWeight = FontWeight.SemiBold,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
            )
            Text(
                text = channel.getMembersStatusText(LocalContext.current, currentUser),
                color = Color.LightGray,
                style = ChatTheme.typography.captionDefault
            )
        }
    }
}
```

## Trailing Content

Next, we'll use the last slot, `trailingContent`, to add video and audio call buttons and a menu button, like WhatsApp has. As this is only an example, these buttons don't do anything. You can check out our [Video SDK](/video/docs/android/) to implement audio & video calling.

```kotlin
ChannelHeader(
    // State
    leadingContent = { CustomHeaderLeadingContent(onClick = onBackClick) },
    centerContent = { CustomHeaderCenterContent(channel = channel, currentUser = currentUser) },
    trailingContent = { CustomHeaderTrailingContent() },
)

@Composable
private fun CustomHeaderTrailingContent() {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .offset(x = 10.dp),
        horizontalArrangement = Arrangement.End
    ) {
        CustomHeaderButton(
            iconRes = R.drawable.ic_videocam,
            contentDescription = "Video Call",
            onClick = {}
        )
        CustomHeaderButton(
            iconRes = R.drawable.ic_phone,
            contentDescription = "Audio Call",
            onClick = {}
        )
        CustomHeaderButton(
            iconRes = R.drawable.ic_menu,
            contentDescription = "Menu",
            onClick = {}
        )
    }
}
```

## Final Touches

As final touches, we change the height and the background color of the header:

```kotlin
ChannelHeader(
    // State
    modifier = Modifier.height(55.dp),
    color = Color(0xFF0F7B6F),
    // Content slots
)
```

<admonition type="warning">

Make sure to use `ChatTheme` as the root of all the composables that use our Compose UI Components. It's used to provide default style properties and utility methods.

</admonition>

## Full code

Below you can find the full code for our implementation.

```kotlin
@Composable
fun CustomChannelHeader(cid: String?, onBackClick: () -> Unit = {}) {
    cid?.let {
        val viewModel = viewModel(
            modelClass = MessageListViewModel::class.java,
            factory = ChannelViewModelFactory(LocalContext.current, channelId = cid)
        )

        val channel = viewModel.channel
        val connectionState by viewModel.connectionState.collectAsStateWithLifecycle()
        val currentUser by viewModel.user.collectAsStateWithLifecycle()

        ChannelHeader(
            channel = channel,
            currentUser = currentUser,
            connectionState = connectionState,
            modifier = Modifier.height(55.dp),
            typingUsers = viewModel.typingUsers,
            messageMode = viewModel.messageMode,
            onBackPressed = onBackClick,
            color = Color(0xFF0F7B6F),
            leadingContent = { CustomHeaderLeadingContent(onClick = onBackClick) },
            centerContent = { CustomHeaderCenterContent(channel = channel, currentUser = currentUser) },
            trailingContent = { CustomHeaderTrailingContent() },
        )
    }
}

@Composable
private fun CustomHeaderLeadingContent(onClick: () -> Unit) {
    CustomHeaderButton(
        iconRes = R.drawable.ic_back,
        contentDescription = "Back",
        onClick = onClick
    )
}

@Composable
private fun CustomHeaderCenterContent(channel: Channel, currentUser: User?) {
    Row {
        ChannelAvatar(
            modifier = Modifier.size(40.dp),
            channel = channel,
            currentUser = currentUser,
        )
        Spacer(modifier = Modifier.size(10.dp))
        Column {
            Text(
                text = ChatTheme.channelNameFormatter.formatChannelName(channel, currentUser),
                color = Color.White,
                fontWeight = FontWeight.SemiBold,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
            )
            Text(
                text = channel.getMembersStatusText(LocalContext.current, currentUser),
                color = Color.LightGray,
                style = ChatTheme.typography.captionDefault
            )
        }
    }
}

@Composable
private fun CustomHeaderTrailingContent() {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .offset(x = 10.dp),
        horizontalArrangement = Arrangement.End
    ) {
        CustomHeaderButton(
            iconRes = R.drawable.ic_videocam,
            contentDescription = "Video Call",
            onClick = {}
        )
        CustomHeaderButton(
            iconRes = R.drawable.ic_phone,
            contentDescription = "Audio Call",
            onClick = {}
        )
        CustomHeaderButton(
            iconRes = R.drawable.ic_menu,
            contentDescription = "Menu",
            onClick = {}
        )
    }
}

@Composable
private fun CustomHeaderButton(@DrawableRes iconRes: Int, contentDescription: String, onClick: () -> Unit) {
    IconButton(
        onClick = onClick,
        content = {
            Icon(
                painter = painterResource(id = iconRes),
                contentDescription = contentDescription,
                tint = Color.White,
            )
        }
    )
}
```

## Integration with ChannelScreen

If you want to use `ChannelScreen` with all its built-in functionality (message list, composer, attachments picker, etc.) but replace only the header, you can do so using the `topBarContent` parameter:

```kotlin
ChannelScreen(
    viewModelFactory = ChannelViewModelFactory(
        context = this,
        channelId = channelId
    ),
    topBarContent = { backAction ->
        CustomChannelHeader(
            cid = channelId,
            onBackClick = backAction
        )
    }
)
```


---

This page was last updated at 2026-04-10T16:24:26.741Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/android/v7/compose-cookbook/custom-channel-header/](https://getstream.io/chat/docs/sdk/android/v7/compose-cookbook/custom-channel-header/).