# Channel Header

The SDK does not render a header inside the [`Channel` component](/chat/docs/sdk/react-native/core-components/channel/) for you — it's up to you to provide it. This cookbook builds a channel header that shows the **channel name** and **image**, a **member/online count**, a **back button** with an **unread badge**, and a **connection status**, all from public APIs.

## Best Practices

- Render the header as a **child** of `<Channel>`, before the `MessageList`, so it sits at the top of the screen and shares the same channel context as the rest of the UI.
- Read channel state through the SDK hooks and components (`useChannelPreviewDisplayName`, `ChannelAvatar`, `useChannelMemberCount`, `useChannelOnlineMemberCount`) instead of reaching into `channel.data` directly — they stay in sync as the channel updates and handle group/1:1 fallbacks for you.
- Keep unread and connection state **reactive**: subscribe to client events (or use the provided hooks) rather than reading a one-time snapshot, so the badge and status update live.
- Apply the top safe-area inset as the header's top margin so it isn't hidden behind the status bar or notch — read it from `useSafeAreaInsets()` ([`react-native-safe-area-context`](https://github.com/AppAndFlow/react-native-safe-area-context)).

## Building the Header

Each piece of the header comes from a hook or component you can use directly:

- **Name** — `useChannelPreviewDisplayName(channel)` returns a ready-to-show name: the channel's name when set, the other user's name for 1:1 chats, or a `"Alice, Bob and 2 others"` style label for groups.
- **Image** — `ChannelAvatar` renders the channel image with built-in fallbacks (channel image, grouped member avatars, or the other user's avatar).
- **Member/online count** — `useChannelMemberCount(channel)` and `useChannelOnlineMemberCount(channel)` give you live counts for the subtitle.
- **Back button** — wrap the `ChevronLeft` icon (exported from `stream-chat-react-native`) in a `Pressable` and call `navigation.goBack()` from [React Navigation](https://reactnavigation.org/).
- **Unread badge** — `BadgeNotification` shows the unread count from the user's _other_ channels (`total_unread_count`), overlaid on the back button so it stays visible after navigating away. A small hook keeps it in sync by listening to client events.
- **Connection status** — `useChatContext()` exposes `isOnline`; use it to show a `Reconnecting…` state when the connection drops.

```tsx
import { useEffect, useState } from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useNavigation } from "@react-navigation/native";
import type { Channel as StreamChatChannel } from "stream-chat";
import {
  BadgeNotification,
  ChannelAvatar,
  ChevronLeft,
  useChannelMemberCount,
  useChannelOnlineMemberCount,
  useChannelPreviewDisplayName,
  useChatContext,
} from "stream-chat-react-native";

// Tracks the unread count across the user's other channels and keeps it in sync.
const useTotalUnreadCount = () => {
  const { client } = useChatContext();
  const [unreadCount, setUnreadCount] = useState(0);

  useEffect(() => {
    const listener = client.on((e) => {
      const event = e.me ?? e;
      if (event.total_unread_count !== undefined) {
        setUnreadCount(event.total_unread_count);
      }
    });
    return () => listener.unsubscribe();
  }, [client]);

  return unreadCount;
};

const ChannelHeader = ({ channel }: { channel: StreamChatChannel }) => {
  const navigation = useNavigation();
  const insets = useSafeAreaInsets();
  const displayName = useChannelPreviewDisplayName(channel);
  const { isOnline } = useChatContext();
  const unreadCount = useTotalUnreadCount();
  const memberCount = useChannelMemberCount(channel);
  const onlineCount = useChannelOnlineMemberCount(channel);

  return (
    <View style={[styles.container, { marginTop: insets.top }]}>
      <Pressable
        accessibilityLabel="Back"
        accessibilityRole="button"
        onPress={() => navigation.goBack()}
        style={styles.backButton}
      >
        <ChevronLeft height={24} width={24} stroke="#080707" />
        {unreadCount > 0 ? (
          <View style={styles.unreadBadge}>
            <BadgeNotification count={unreadCount} size="sm" type="primary" />
          </View>
        ) : null}
      </Pressable>

      <ChannelAvatar channel={channel} />

      <View style={styles.titleContainer}>
        <Text style={styles.title} numberOfLines={1}>
          {displayName}
        </Text>
        <Text style={styles.status}>
          {isOnline
            ? `${memberCount} members, ${onlineCount} online`
            : "Reconnecting…"}
        </Text>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    alignItems: "center",
    borderBottomColor: "#E9EAEA",
    borderBottomWidth: 1,
    flexDirection: "row",
    gap: 8,
    paddingHorizontal: 12,
    paddingVertical: 8,
  },
  backButton: {
    alignItems: "center",
    height: 40,
    justifyContent: "center",
    width: 40,
  },
  unreadBadge: {
    left: 24,
    position: "absolute",
    top: 2,
  },
  titleContainer: {
    flex: 1,
  },
  title: {
    fontSize: 16,
    fontWeight: "600",
  },
  status: {
    fontSize: 12,
  },
});
```

## Adding the Header to the Channel

Place the header **inside** `<Channel>`, above the `MessageList`:

```tsx
import {
  Channel,
  MessageComposer,
  MessageList,
} from "stream-chat-react-native";

const ChannelScreen = ({ channel }) => (
  <Channel channel={channel}>
    <ChannelHeader channel={channel} />
    <MessageList />
    <MessageComposer />
  </Channel>
);
```


---

This page was last updated at 2026-07-01T15:44:28.913Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/guides/channel-header/](https://getstream.io/chat/docs/sdk/react-native/guides/channel-header/).