# Custom Channel Header

The channel header appears above the message list in a chat view, and it has many different variations across chat apps.

In this example, we will rebuild the one from WhatsApp. The end result will look like this:

![Screenshot of the channel header.](@chat-sdk/ios/v5/_assets/whatsapp-header.png)

First, let's create a new file, called `WhatsAppChannelHeader`. This struct will implement the `ToolbarContent` protocol from SwiftUI, which is required for customizing the navigation bar.

```swift
import StreamChat
import StreamChatSwiftUI
import SwiftUI

struct WhatsAppChannelHeader: ToolbarContent {
	var body: some ToolbarContent {
		ToolbarItem(placement: .principal) {
			Text("Channel")
		}
	}
}
```

The channel header from above consists of three sections:

- leading section, represented by the back button. This one is added automatically for push navigation, so no additional changes are needed here.
- title section (also called principal placement in SwiftUI), represented by the channel icon and its name and participant info.
- trailing section, represented by the icons for starting audio and video calls.

### Principal section

First, let's implement the principal section. If you take a closer look at the UI, you will notice that it has two columns:

- the channel icon.
- the channel info, which in turn has two rows.
  Therefore, we will need an `HStack` for the columns, and a `VStack` for the title rows.

For the channel icon, we can use the `ChannelAvatar` from our SwiftUI SDK as a building block. You can easily provide your custom implementation if you look for a different UI.

Additionally, we will need a few other objects from the SwiftUI SDK, for getting the user info, fonts, colors and utilities. To use these, add the following lines at the top of your struct:

```swift
@Injected(\.chatClient) var chatClient
@Injected(\.utils) var utils
@Injected(\.fonts) var fonts
@Injected(\.colors) var colors
```

The state for the channel will be passed as a parameter to this toolbar content, so we should add it as a variable as well.

```swift
var channel: ChatChannel
```

Next, we will add few computed variables, that will be used for setting up the channel name and the subtitle.

```swift
private var currentUserId: String {
    chatClient.currentUserId ?? ""
}

private var channelNameFormatter: ChannelNameFormatter {
    utils.channelNameFormatter
}

private var channelSubtitle: String {
    if channel.memberCount <= 2 {
        return channel.onlineInfoText(currentUserId: currentUserId)
    } else {
        return channel
            .lastActiveMembers
            .map { $0.name ?? $0.id }
            .joined(separator: ", ")
    }
}
```

Finally, we can add the `ToolbarItem` itself, with the placement of `.principal`. Here's the implementation that uses the things we have defined so far. Replace the placeholder body of the `ToolbarContent` with this code.

```swift
ToolbarItem(placement: .principal) {
    HStack {
        ChannelAvatar(
            channel: channel,
            size: 36,
            indicator: .none
        )
        VStack(alignment: .leading) {
            Text(channelNameFormatter.format(channel: channel, forCurrentUserId: currentUserId) ?? "")
                .font(fonts.bodyBold)
            Text(channelSubtitle)
                .font(fonts.caption1)
                .foregroundColor(Color(colors.textTertiary))
        }
    }
}
```

### Trailing Section

Next, let's add the trailing section, which will have two buttons in a horizontal stack, for video and audio calling.

```swift
ToolbarItem(placement: .topBarTrailing) {
    HStack {
        Button(action: {
            print("tapped on video")
        }, label: {
            Image(systemName: "video")
        })
        Button(action: {
            print("tapped on audio")
        }, label: {
            Image(systemName: "phone")
        })
    }
}
```

Integrating audio and video calling is outside the scope of this cookbook. If you are interested in integrating our `StreamVideo` SDK for audio and video calling, please check our [docs](/video/docs/ios/).

Here's the full implementation of the toolbar content for cross-checking.

```swift
struct WhatsAppChannelHeader: ToolbarContent {

    @Injected(\.chatClient) var chatClient
    @Injected(\.utils) var utils
    @Injected(\.fonts) var fonts
    @Injected(\.colors) var colors

    var channel: ChatChannel

    private var currentUserId: String {
        chatClient.currentUserId ?? ""
    }

    private var channelNameFormatter: ChannelNameFormatter {
        utils.channelNameFormatter
    }

    private var channelSubtitle: String {
        if channel.memberCount <= 2 {
            return channel.onlineInfoText(currentUserId: currentUserId)
        } else {
            return channel
                .lastActiveMembers
                .map { $0.name ?? $0.id }
                .joined(separator: ", ")
        }
    }

    var body: some ToolbarContent {
        ToolbarItem(placement: .principal) {
            HStack {
                ChannelAvatar(
                    channel: channel,
                    size: 36,
                    indicator: .none
                )
                VStack(alignment: .leading) {
                    Text(channelNameFormatter.format(channel: channel, forCurrentUserId: currentUserId) ?? "")
                        .font(fonts.bodyBold)
                    Text(channelSubtitle)
                        .font(fonts.caption1)
                        .foregroundColor(Color(colors.textTertiary))
                }
            }
        }
        ToolbarItem(placement: .topBarTrailing) {
            HStack {
                Button(action: {
                    print("tapped on video")
                }, label: {
                    Image(systemName: "video")
                })
                Button(action: {
                    print("tapped on audio")
                }, label: {
                    Image(systemName: "phone")
                })
            }
        }
    }
}
```

### Channel Header Modifier

The next step would be to create a channel header view modifier, which would be injected into our SwiftUI SDK.

```swift
struct WhatsAppChannelHeaderModifier: ChatChannelHeaderViewModifier {

    let channel: ChatChannel

    func body(content: Content) -> some View {
        content.toolbar {
            WhatsAppChannelHeader(channel: channel)
        }
    }
}
```

Finally, we need to provide this modifier to the SDK, which will use it instead of the default one. For this, create a new file called `CustomViewFactory` and implement the `makeChannelHeaderViewModifier` from the `ViewFactory` protocol.

```swift
import StreamChat
import StreamChatSwiftUI
import SwiftUI

class CustomViewFactory: ViewFactory {

    @Injected(\.chatClient) public var chatClient
    public var styles = RegularStyles()

    func makeChannelHeaderViewModifier(options: ChannelHeaderViewModifierOptions) -> some ChatChannelHeaderViewModifier {
        WhatsAppChannelHeaderModifier(channel: options.channel)
    }
}
```

Depending on which components you use, you should provide this view factory instead of the default one. For example, if you are using a `ChatChannelListView`, you can pass it in the creation process.

```swift
var body: some Scene {
    WindowGroup {
        ChatChannelListView(viewFactory: CustomViewFactory())
    }
}
```

If you run your app now, you should see the updated channel header, as shown on the screenshot.

![Screenshot of the channel header.](@chat-sdk/ios/v5/_assets/whatsapp-header.png)

## Summary

In this cookbook, you learnt how to implement your own version of the channel view header, using the `ToolbarContent` API from SwiftUI. We have also shown you how to integrate it with our SwiftUI SDK.

As a next step, you can explore other parts of our cookbook, where we build many interesting customizations. Furthermore, for a complete social experience, we recommend looking into our [Video SDK](/video/docs/ios/).


---

This page was last updated at 2026-04-22T16:42:53.970Z.

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