Channel details lists
Channel details has five lists, the member list, the add members form, and the pinned messages, photos and videos, and files lists. ChannelDetails opens each in a modal, and each is exported so a screen of your own can render it. This page lists their parts, override keys and props. ChannelDetails covers the screen that opens them.
Best practices
- Render a list inside a
ChannelDetailsContextProviderfor the channel. It reads the channel from there, and renders nothing until the channel has acid. - Give the list a container with a height, such as a screen with
flex: 1. The lists scroll inside it. - Replace a row through its override key, such as
PinnedMessageItem, rather than the whole list. The list keeps its loading, paging, search and empty states. - To change what a list loads, pass your own search source as
searchSource. Channel details contexts shows the default sources.
How the lists load
Each list loads from a search source of stream-chat, which it creates when it mounts:
| List | Override key | Opened from | Loads |
|---|---|---|---|
ChannelMemberList |
ChannelMemberList |
View all in the members section | The channel's members, by name, with a ChannelMemberSearchSource |
ChannelAddMembersForm |
ChannelAddMembersForm |
Add, in the members section and the member list | Users, by name, with a UserSearchSource |
PinnedMessageList |
PinnedMessageList |
Pinned Messages | The channel's pinned messages, with a MessageSearchSource, searched by text |
MediaList |
MediaList |
Photos & Videos | The channel's messages with images or videos, newest first, with a MessageSearchSource |
FileAttachmentList |
FileAttachmentList |
Files | The channel's messages with files or audio, newest first, with a MessageSearchSource |
Each source loads 25 items a page, and the list loads the next page when it's scrolled near its end, with a spinner at the bottom meanwhile. The member list, the add members form and the pinned messages list have a SearchInput that searches as the user types. The first load shows a skeleton. The pinned messages, photos and videos, and files lists show EmptyList when they have nothing, and the pinned messages list shows EmptySearchResult for a search that matched nothing. The member list and the add members form show EmptySearchResult whenever they're empty.
The sources are a snapshot. They don't change as events arrive, so a message pinned while the list is open shows the next time it opens. The member list is the exception. It loads its first page again when the channel's member count changes, while no search is typed, so members added from it show at once.
When a query fails, the list publishes an error notification, such as api:channel:query-members:failed, to a NotificationList of its own. Each list is its own notification target in the channel-details panel.
Members
ChannelMemberList
The searchable list of the channel's members, each through the ChannelMemberItem key. Pressing a member opens the ChannelMemberActionsSheet key for them.
| Prop | Type | Default | Description |
|---|---|---|---|
searchSource |
ChannelMemberSearchSource |
the channel's members, sorted by name | The source to load from. |
searchInputProps |
SearchInputProps |
Passed to the SearchInput, after the list's own props, so onChangeText replaces the search. |
|
additionalFlatListProps |
Partial<FlatListProps<ChannelMemberResponse>> |
Passed to the FlatList, after the list's own props. |
ChannelMemberItem
A member row shows a UserAvatar, with a presence dot while the user is online, the name, or "You" for the current user, the activity status from useUserActivityStatus, a mute icon while the user is muted, and the RoleList key with the member's roles. With onPress it's a button; without one, a plain row.
| Prop | Type | Default | Description |
|---|---|---|---|
member |
ChannelMemberResponse |
required | The member. A member without a user renders nothing. |
onPress |
(member: ChannelMemberResponse) => void |
Makes the row pressable. | |
size |
ChannelMemberItemSize |
'sm' |
'sm', a list row with an md avatar, or 'lg', a header with an lg avatar, as at the top of the member sheet. |
getMemberRoles |
GetMemberRoles |
the default roles | ({ channel, defaultRoleLabels, member, t }) => roles, the roles to show. [] shows none. |
testID |
string |
Set on the row. |
The default roles are Owner for the channel's creator, Admin for a user whose role is admin, and Moderator for a member whose channel_role is channel_moderator. Custom member roles shows others.
RoleList and RoleItem
RoleList renders a RoleItem key for each role, and nothing without roles. RoleItem is a pill with the role's label, in the brand colors for the owner key and in neutral colors otherwise.
| Prop | Type | Default | Description |
|---|---|---|---|
roles |
RoleLabel[] |
required | The roles, each { key, label }. |
RoleItemProps:
| Prop | Type | Default | Description |
|---|---|---|---|
role |
RoleLabel |
required | The role. |
textStyle |
StyleProp<TextStyle> |
Applied to the label, after the theme. | |
viewStyle |
StyleProp<ViewStyle> |
Applied to the pill, after the theme. |
ChannelMemberActionsSheet
A bottom sheet with the member as a lg ChannelMemberItem and the member's actions as ChannelDetailsActionItem rows. Pressing an action runs it and closes the sheet. The actions come from useChannelMemberActionItems: mute or unmute and block or unblock the user, and remove the member with the update-channel-members capability. The current user's own sheet has no actions.
| Prop | Type | Default | Description |
|---|---|---|---|
member |
ChannelMemberResponse |
required | The member. |
visible |
boolean |
required | Whether the sheet is open. |
onClose |
() => void |
required | Called when the sheet closes, and after an action. |
getChannelMemberActionItems |
GetChannelMemberActionItems |
({ context, defaultItems }) => items, the actions to show. See Member actions. |
Adding members
ChannelAddMembersButton
Renders Add, or a user-plus icon, when the user has the update-channel-members capability, and nothing otherwise. Pressing it opens the ChannelAddMembersForm key in a modal.
| Prop | Type | Default | Description |
|---|---|---|---|
variant |
'icon' | 'text' |
'text' |
An outlined Add button, or an icon-only button. |
onPress |
() => void |
Called in place of opening the form. The button then renders no modal. | |
style |
ButtonProps['style'] |
Passed to the Button. |
|
testID |
string |
Passed to the Button. |
The members section renders it with variant="text", and the header of the member list with variant="icon".
ChannelAddMembersForm
Wraps its parts in a ChannelAddMembersProvider, and renders the ChannelAddMembersFormHeader key, the ChannelAddMembersFormContent key and a NotificationList.
| Prop | Type | Default | Description |
|---|---|---|---|
onClose |
() => void |
required | Called by the close button, and after members are added. |
searchSource |
UserSearchSource |
users, sorted by name | The source to load from. It's used as it is, so activate it and set its sort yourself. |
ChannelAddMembersFormHeader
Close, "Add Members", and a confirm button. The confirm button is enabled while at least one user is selected. It calls submit() of the add members context, which adds the selected users with Channel.addMembers({ members }), and closes the form when it succeeds.
| Prop | Type | Default | Description |
|---|---|---|---|
onClose |
() => void |
required | Called by the close button, and after members are added. |
ChannelAddMembersFormContent
The SearchInput and the list of users, each an AddMemberSearchResultItem. It searches with an empty query when it mounts, so the list starts with every user the source returns.
| Prop | Type | Default | Description |
|---|---|---|---|
additionalFlatListProps |
Partial<FlatListProps<UserResponse>> |
Passed to the FlatList, after the list's own props. |
AddMemberSearchResultItem
A user row with a UserAvatar, the name and a SelectionCircle. Pressing it calls onPress, which the form uses to toggle the user in its selection. A user who is already a member shows "Already a member" and can't be pressed. It has no override key, because ChannelAddMembersFormContent renders it directly.
| Prop | Type | Default | Description |
|---|---|---|---|
user |
UserResponse |
required | The user. |
onPress |
(user: UserResponse) => void |
required | Called when the row is pressed. |
It reads the selection from useChannelAddMembersContext(), so it renders inside a ChannelAddMembersProvider.
Pinned messages
PinnedMessageList
The searchable list of the channel's pinned messages, each through the PinnedMessageItem key. While the channel has no pinned messages, it shows an EmptyList and no search field.
| Prop | Type | Default | Description |
|---|---|---|---|
searchSource |
MessageSearchSource |
the channel's pinned messages | The source to load from. |
searchInputProps |
SearchInputProps |
Passed to the SearchInput, after the list's own props. |
|
additionalFlatListProps |
Partial<FlatListProps<MessageResponse>> |
Passed to the FlatList, after the list's own props. |
PinnedMessageItem
A row with the sender's UserAvatar, the sender's name, and the message's preview and time through the ChannelPreviewLastMessage and ChannelPreviewStatus keys. It shows the message's translation in the user's language when there is one. It isn't pressable. To jump to the message, override the key with a row that navigates, as Pinned messages shows.
| Prop | Type | Default | Description |
|---|---|---|---|
channel |
Channel |
required | The channel. |
message |
MessageResponse |
required | The pinned message. |
formatMessageDate |
ChannelPreviewStatusProps['formatLatestMessageDate'] |
the channel preview's format | (date: Date) => string, formats the time. |
The list passes channel and message.
Photos and videos
MediaList
A grid of three columns, one MediaItem key per image or video attachment, from useMediaList. Pressing an image or a video opens the image gallery on it, with every loaded message to swipe through. Without a video player installed, pressing a video opens its URL instead.
| Prop | Type | Default | Description |
|---|---|---|---|
searchSource |
MessageSearchSource |
the channel's messages with images or videos, newest first | The source to load from. |
additionalFlatListProps |
Partial<FlatListProps<MediaTile>> |
Passed to the FlatList, after the list's own props. |
It opens the gallery through ImageGalleryContext and OverlayContext, so it renders inside OverlayProvider. On a screen of your own, OverlayProvider's gallery shows over it.
MediaItem
A square tile with the attachment's thumbnail, resized through the CDN to the tile's size, the sender's sm UserAvatar, and, for a video, its duration.
| Prop | Type | Default | Description |
|---|---|---|---|
attachment |
Attachment |
required | The image or video attachment. |
message |
MessageResponse |
required | Its message. |
size |
number |
required | The side of the tile, in points. |
onPress |
(params: MediaItemPressParams) => void |
Called when the tile is pressed. Without it, the tile does nothing. |
MediaItemPressParams is { attachment, message, requesterNode }. requesterNode is the tile's native handle, for the gallery's openImageGallery().
Files
FileAttachmentList
A list of the file and audio attachments, under a header for each month, from useFileAttachmentListSections. Each attachment renders through the FileAttachmentItem key, and each header through FileAttachmentListSectionHeader, which has no override key. The headers stick to the top while their month scrolls.
| Prop | Type | Default | Description |
|---|---|---|---|
searchSource |
MessageSearchSource |
the channel's messages with files or audio, newest first | The source to load from. |
additionalSectionListProps |
Partial<SectionListProps<FileAttachmentTile, FileAttachmentSection>> |
Passed to the SectionList, after the list's own props. |
FileAttachmentItem
A row with the file's icon, name and size. Pressing it opens the attachment's asset_url.
| Prop | Type | Default | Description |
|---|---|---|---|
attachment |
Attachment |
required | The file or audio attachment. |
message |
MessageResponse |
required | Its message. |
onPress |
(params: FileAttachmentItemPressParams) => void |
opens asset_url |
Called in place of opening the URL, with { attachment, message }. |
FileAttachmentListSectionHeader
| Prop | Type | Default | Description |
|---|---|---|---|
title |
string |
required | The section's title, such as "March 2026". |
Loading skeletons
The lists show these while their first page loads. They take no props, have no override key, and are styled by the theme key in brackets: MemberListLoadingSkeleton (memberListSkeleton), UserListLoadingSkeleton (userListSkeleton), PinnedMessageListLoadingSkeleton (pinnedMessageListSkeleton), FileAttachmentListLoadingSkeleton (fileAttachmentListSkeleton) and MediaListLoadingSkeleton.
Theming
The theme's channelDetails key styles the lists and rows: memberList, memberItem, roleList, roleItem, memberActionsSheet, addMembers, pinnedMessageList, pinnedMessageItem, mediaList, mediaItem, fileAttachmentList and fileAttachmentItem. See Theme object.
Example
A files screen of your own, which opens files in your viewer:
import { View } from "react-native";
import type { Channel as StreamChannel } from "stream-chat";
import {
ChannelDetailsContextProvider,
FileAttachmentItem,
FileAttachmentList,
WithComponents,
} from "stream-chat-react-native";
import type {
ComponentOverrides,
FileAttachmentItemProps,
} from "stream-chat-react-native";
type Props = {
channel: StreamChannel;
openFile: (url: string) => void;
};
export const ChannelFilesScreen = ({ channel, openFile }: Props) => {
const FileRow = (props: FileAttachmentItemProps) => (
<FileAttachmentItem
{...props}
onPress={({ attachment }) => {
if (attachment.asset_url) openFile(attachment.asset_url);
}}
/>
);
const overrides: ComponentOverrides = { FileAttachmentItem: FileRow };
return (
<ChannelDetailsContextProvider channel={channel}>
<WithComponents overrides={overrides}>
<View style={{ flex: 1 }}>
<FileAttachmentList />
</View>
</WithComponents>
</ChannelDetailsContextProvider>
);
};WithComponents reads overrides when it mounts, so the row keeps the openFile of the first render. Pass a function that stays the same, such as one from useCallback or your navigator.
Exports
| Export | What it is |
|---|---|
ChannelMemberList, ChannelMemberItem, ChannelMemberActionsSheet, RoleList, RoleItem |
The members |
ChannelAddMembersButton, ChannelAddMembersForm, ChannelAddMembersFormHeader, ChannelAddMembersFormContent, AddMemberSearchResultItem |
Adding members |
PinnedMessageList, PinnedMessageItem |
Pinned messages |
MediaList, MediaItem |
Photos and videos |
FileAttachmentList, FileAttachmentItem, FileAttachmentListSectionHeader |
Files |
MemberListLoadingSkeleton, UserListLoadingSkeleton, PinnedMessageListLoadingSkeleton, MediaListLoadingSkeleton, FileAttachmentListLoadingSkeleton |
Loading states |
The props types are exported with the components' names plus Props, as ChannelMemberListProps, except for the skeletons. ChannelMemberItemSize, RoleLabel, GetMemberRoles, MediaItemPressParams, FileAttachmentItemPressParams, MediaTile, FileAttachmentTile and FileAttachmentSection are exported too.
Related hooks
| Hook | Returns |
|---|---|
useMemberRoles(member, getMemberRoles?) |
the roles of a member row |
useChannelMemberActionItems({ channel, member }) |
the actions of the member sheet |
useUserActivityStatus(user) |
"Online", when the user was last active, or "Offline" |
useMediaList(messages) |
one tile per image or video attachment |
useFileAttachmentListSections(messages) |
the file and audio attachments, by month |
useChannelMemberListContext(), useChannelAddMembersContext(), useChannelPinnedMessageListContext(), useChannelMediaListContext(), useChannelFileAttachmentListContext() |
the search source of each list, and the add members selection |
They're on Channel details hooks. The contexts are on Channel details contexts.