Channel

Channel is the main entry point for customization and hosts most of the contexts and logic used by the React Native SDK. MessageList, MessageComposer, and Thread require it. It provides the following contexts to its children:

  • ChannelContext (via useChannelContext) - channel state and configuration
  • KeyboardContext (via useKeyboardContext) - keyboard handling state
  • MessageInputContext (via useMessageInputContext) - message input state and actions
  • MessagesContext (via useMessagesContext) - message rendering configuration
  • PaginatedMessageListContext (via usePaginatedMessageListContext) - message pagination state
  • AttachmentPickerContext (via useAttachmentPickerContext) - attachment picker state
  • MessageComposerContext (via useMessageComposerAPIContext) - message composer API
  • ThreadContext (via useThreadContext) - thread state
  • TypingContext (via useTypingContext) - typing indicator state

Best Practices

  • Create and watch channels once, then reuse the instance across renders.
  • Render Channel under OverlayProvider and Chat to ensure all contexts are available.
  • Customize UI by replacing specific components via WithComponents instead of re-implementing core logic.
  • Keep custom components lightweight to avoid performance regressions in lists.
  • Use channel props for behavior changes before reaching for custom overrides.

Basic Usage

Channel takes a StreamChat channel instance. Create one via creating channels or read it from ChannelList via the onSelect callback.

import { useEffect, useState } from "react";
import { StreamChat } from "stream-chat";
import {
  Channel,
  Chat,
  MessageComposer,
  MessageList,
  OverlayProvider,
} from "stream-chat-react-native";

const client = StreamChat.getInstance("api_key");

export const App = () => {
  const [channel, setChannel] = useState();

  useEffect(() => {
    const createAndWatchChannel = async () => {
      const newChannel = client.channel("messaging", "channel_id");
      await newChannel.watch();
      setChannel(newChannel);
    };

    createAndWatchChannel();
  }, []);

  return (
    <OverlayProvider>
      <Chat client={client}>
        <Channel channel={channel}>
          <MessageList />
          <MessageComposer />
        </Channel>
      </Chat>
    </OverlayProvider>
  );
};

Registering Custom Components

Component overrides are provided via the WithComponents wrapper instead of props on Channel. Wrap Channel (or any ancestor) with <WithComponents overrides={{ ... }}> and the custom components will be used throughout the SDK where applicable.

Customizing the message author can be done by providing a custom component to the overrides prop on WithComponents.

import { Image } from "react-native";
import {
  Channel,
  MessageComposer,
  MessageList,
  WithComponents,
  useMessageContext,
} from "stream-chat-react-native";

const CustomAuthor = () => {
  const { message } = useMessageContext();

  return <Image source={{ uri: message.user?.image }} />;
};

<WithComponents overrides={{ MessageAuthor: CustomAuthor }}>
  <Channel channel={channel}>
    <MessageList />
    <MessageComposer />
  </Channel>
</WithComponents>;

WithComponents can be placed at any level of the tree. Overrides are merged with any parent WithComponents providers, so you can set global defaults at the top and refine them deeper in the tree.

Props

PropDescriptionType
additionalKeyboardAvoidingViewPropsExtra props passed to KeyboardAvoidingView.object
additionalPressablePropsExtra props passed to the underlying Pressable used in message components like MessageContent.object
additionalTextInputPropsExtra props passed to the underlying TextInput in MessageComposer.object
allowConcurrentAudioPlaybackAllow multiple audio players to play at once. Defaults to false.boolean
allowSendBeforeAttachmentsUploadWhen enabled, the user can send a message before all attachments have finished uploading. Defaults to the value of enableOfflineSupport from Chat.boolean
allowThreadMessagesInChannelShow the Show thread message in channel button in thread MessageComposer. Defaults to true.boolean
asyncMessagesLockDistancePixels the user must drag upward to lock recording and lift their finger without stopping it. Defaults to 50.number
asyncMessagesMinimumPressDurationMinimum press duration (ms) on the record button to start voice recording. Defaults to 500.number
asyncMessagesSlideToCancelDistancePixels the user must drag toward the leading side to cancel voice recording. Defaults to 75.number
attachmentPickerBottomSheetHeightHeight of the attachment picker bottom sheet when open. Defaults to 333.number
attachmentSelectionBarHeightHeight of the attachment selection bar in the attachment picker. Defaults to 72.number
audioRecordingEnabledEnable or disable audio recording. Defaults to false.boolean
audioRecordingSendOnCompleteWhen true, sends voice recording immediately after upload. When false, keeps it in the composer. Defaults to true.boolean
bottomInsetHeight of items below MessageComposer. Determines the shift to MessageList when the attachment picker opens. Update after mount via setBottomInset from useAttachmentPickerContext. Defaults to 0.number
channelChannel instance from the Stream Chat client.Channel
compressImageQualityImage compression quality before upload (0–1, where 1 is best). On iOS, values ≥ 0.8 keep quality while reducing size. Defaults to 0.8 on iOS, 1 on Android.number
createPollOptionGapGap between options in the poll creation dialog. Defaults to 8.number
customMessageSwipeActionCustom action executed when the user swipes a message row. Default action is swipe to reply.({channel: Channel, message: LocalMessage}) => void
disableAttachmentPickerWhen true, the attachment picker is hidden. Defaults to true when the image media library is not available.boolean
disableKeyboardCompatibleViewDisable the underlying KeyboardAvoidingView. Defaults to false.boolean
disableTypingIndicatorDisable the typing indicator in MessageList. Defaults to false.boolean
dismissKeyboardOnMessageTouchDismiss the keyboard when the user touches a message in the list. Defaults to true.boolean
doFileUploadRequestOverride the file upload request when a user selects a file. Use this to store files in your own CDN. See example below.UploadRequestFn
doMarkReadRequestOverride the mark read request. Use only for conditional mark-read logic; do not use to disable read receipts (use Read Events on the dashboard instead). See example below.(channel: Channel, setChannelUnreadUiState?: (state) => void) => void
doSendMessageRequestOverride the send message request. Must return a Promise equivalent to channel.sendMessage. See example below.(channelId: string, message: object, options?: object) => Promise
doUpdateMessageRequestOverride the update message request. Must return a Promise equivalent to client.updateMessage. See example below.(channelId: string, message: object, options?: object) => Promise
enableMessageGroupingByUserIf false, consecutive messages from the same user won't be grouped. Defaults to true.boolean
enableSwipeToReplyIf true, users can swipe the full MessageItemView row to reply. Defaults to true.boolean
enforceUniqueReactionLimits reactions to one per user. Selecting a new reaction replaces the previous one. Defaults to false.boolean
forceAlignMessagesForces all messages to align left or right. By default, received messages are left and sent messages are right. Defaults to false.'left' | 'right' | false
getMessageGroupStyleOverride message group position logic (top, bottom, middle, single). See default logic in getGroupStyles. Access computed styles via MessageContext.groupStyles.function
giphyVersionGiphy image version to render. See Image Object keys. Defaults to 'fixed_height'.string
handleAttachButtonPressCustom behavior when the AttachButton is pressed.() => void
handleBanCalled when Ban User is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleBlockUserCalled when Block User is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleCopyCalled when Copy Message is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleDeleteCalled when Delete Message is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleDeleteForMeCalled when Delete Message for me is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleEditCalled when Edit Message is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleFlagCalled when Flag Message is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleMarkUnreadCalled when Mark Unread is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleMuteCalled when Mute User is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handlePinMessageCalled when Pin to Conversation or Unpin from Conversation is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleQuotedReplyCalled when Reply is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleReactionCalled when a reaction is selected (add or remove). Does not override default behavior. See customize message actions.(message: LocalMessage, reactionType: string) => void
handleRetryCalled when Retry is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
handleThreadReplyCalled when Thread Reply is triggered. Does not override default behavior. See customize message actions.(message: LocalMessage) => void
hasCameraPickerEnable the camera picker in MessageComposer. Defaults to true.boolean
hasCommandsEnable commands in MessageComposer. Defaults to true.boolean
hasCreatePollControls whether the poll creation button is visible.boolean
hasFilePickerEnable the file picker in MessageComposer. Defaults to true.boolean
hasImagePickerEnable the image picker in MessageComposer. Defaults to true.boolean
hideDateSeparatorsHide inline date separators in MessageList. Defaults to false.boolean
hideStickyDateHeaderHide the sticky date header in MessageList. Defaults to false.boolean
initialScrollToFirstUnreadMessageLoad the channel starting at the first unread message. Defaults to false.boolean
initializeOnMountWhen false, Channel skips channel.watch() on mount; you must call it manually. Useful for read-only or ephemeral channels. Defaults to true.boolean
isAttachmentEqualReturns true if rendering nextAttachment would produce the same result as prevAttachment.(prevAttachment: Attachment, nextAttachment: Attachment) => boolean
isMessageAIGeneratedDetermines whether a message was AI-generated. Used to enable streaming via StreamingMessageView. Defaults to () => false.(message: MessageType) => boolean
keyboardBehaviorBehavior for the keyboard passed to KeyboardAvoidingView.'height' | 'position' | 'padding'
keyboardVerticalOffsetVertical offset used by the built-in keyboard handling. Set to your header height for consistent behavior across platforms.number
loadingMoreOverride the loadingMore value supplied by the channel query logic.boolean
loadingMoreRecentOverride the loadingMoreRecent value supplied by the channel query logic.boolean
markdownRulesRules for simple-markdown.object
markReadOnMountEnable/disable marking the channel as read when Channel mounts. Defaults to true.boolean
maximumMessageLimitLimits how many messages are kept in memory. Oldest messages are pruned when exceeded (pagination still works). Messages are not pruned near the top of the list. Useful for high-traffic channels.number
maxTimeBetweenGroupedMessagesMaximum time (ms) between consecutive messages from the same user to keep them grouped. Defaults to infinite.number
messageActionsAn array of actions, or a function returning an array, shown in the message menu. See customize message actions and the default actions.Array | (actionInfo: object) => Array
messageContentOrderOrder for rendering message content. Defaults to ['quoted_reply', 'gallery', 'files', 'poll', 'ai_text', 'attachments', 'text', 'location'].string[]
messageIdLoad the channel at a specified message instead of the most recent message.string
messageInputFloatingWhen true, renders the message input in a floating style. Defaults to false.boolean
messageSwipeToReplyHitSlopDefines the hitSlop area for the full-row swipe-to-reply gesture. Defaults to {left: screenWidth, right: screenWidth}.{top: number, left: number, bottom: number, right: number}
messageTextNumberOfLinesNumber of lines for message text in the Message Overlay. Defaults to 5.number
myMessageThemeTheme applied to the current user's messages. Memoize this object or pass a stable reference.object
newMessageStateUpdateThrottleIntervalDeprecated. Throttle interval (ms) for channel state updates when new messages arrive. Defaults to 500.number
numberOfAttachmentImagesToLoadPerCallNumber of images loaded per CameraRoll.getPhotos call. Defaults to 25.number
numberOfAttachmentPickerImageColumnsNumber of columns in the image picker. Defaults to 3.number
onAlsoSentToChannelHeaderPressCalled when the "Also sent to channel" header is pressed in a thread message.function
onLongPressMessageCalled when a user long-presses a message. The default opens the message menu.(payload: {actionHandlers: object, message: LocalMessage}) => void
onPressInMessageCalled on touch start, before onPressMessage.(payload: {actionHandlers: object, message: LocalMessage}) => void
onPressMessageCalled when a user presses a message. The default handler differs for reactions and attachments — handle those cases if you override it. See example below.(payload: {additionalInfo: object, actionHandlers: object, message: LocalMessage}) => void
openPollCreationDialogCalled when the poll creation button is clicked in the attachment picker. Receives {sendMessage} from MessageInputContext for use in CreatePoll.(payload: {sendMessage: function}) => void
overrideOwnCapabilitiesOverride the current user's capabilities (normally derived from permissions, channel type, and settings). See available keys below.object
preSendMessageRequestInvoked after the first optimistic update of a new message, but before any HTTP requests. Useful for creating a channel or editing a message before sending.(options: {localMessage: LocalMessage, message: StreamMessage, options?: SendMessageOptions}) => void
reactionListPositionPosition of the reaction list relative to message content. Defaults to 'top'.'top' | 'bottom'
reactionListTypeVisual style of the reaction list. Defaults to 'clustered'.'clustered' | 'simple'
selectReactionFull override of the message reaction handler. Must return a function that accepts reactionType. See customize message actions.((message: LocalMessage) => (reactionType: string) => void) | null
setInputRefCallback to set the ref on the underlying TextInput in MessageComposer.(ref: TextInput) => void
shouldShowUnreadUnderlayEnable/disable the unread underlay background in the message list. Defaults to true.boolean
stateUpdateThrottleIntervalThrottle interval (ms) for channel state updates (excluding new messages). Defaults to 500.number
supportedReactionsList of reactions users can add to messages. See customizing reactions. Defaults to reactionData.array
threadThread message or ThreadType. Setting it indicates a thread is open. Set thread on all Channel components when a thread is open.LocalMessage | ThreadType
threadListIndicates Channel is rendering a thread. Used to avoid concurrency issues between the main channel and thread.boolean
topInsetDistance from the top of the screen the attachment picker should open to when expanded. Often set to the header height. Update after mount via setTopInset from useAttachmentPickerContext. Defaults to 0.number
urlPreviewTypeControls the type of URL preview shown for messages with links. Defaults to 'full'.'full' | 'compact'

Examples

Override the file upload request

The doFileUploadRequest prop accepts a function matching the UploadRequestFn type from the stream-chat package:

export type UploadRequestFn = (
  fileLike: FileReference | FileLike,
) => Promise<MinimumUploadRequestResult>;

Conditional mark-read requests

const doMarkReadRequest = (channel) => {
  if (/** Some custom logic here */) {
    channel.markRead();
  }
};

Override the send message request

const doSendMessageRequest = (channelId, messageObject) => {
  if (/** Some custom logic here */) {
    messageObject.isSpecial = true;
  }
  return channel.sendMessage(messageObject);
}

Override the update message request

const doUpdateMessageRequest = (channelId, messageObject) => {
  const numberOfUpdates = (messageObject.numberOfUpdates ?? 0) + 1;
  const messageToSend = { ...messageObject, numberOfUpdates };
  return client.updateMessage(messageToSend);
};

Custom message press handler

additionalInfo provides extra data for certain emitters (for example, user details for textMention).

<Channel
  onPressMessage={({ additionalInfo, defaultHandler, emitter }) => {
    if (emitter === 'textMention') {
      console.log(additionalInfo?.user);
      return;
    }

    if (emitter === 'card' || emitter === 'textLink') {
      console.log(additionalInfo?.url);
      return;
    }

    if (emitter === 'fileAttachment') {
      console.log(additionalInfo?.attachment);
      return;
    }

    defaultHandler?.();
  }}
>

additionalInfo may change as we add more emitter use cases.

Override user capabilities

<Channel
  overrideOwnCapabilities={{
    uploadFile: false,
    sendLinks: false
  }}

Available keys:

  • banChannelMembers — When false, "Block User" is hidden in the message menu.
  • deleteAnyMessage — When false, "Delete Message" is hidden for received messages.
  • deleteOwnMessage — When false, "Delete Message" is hidden for own messages.
  • flagMessage — When false, "Flag Message" is hidden.
  • pinMessage — When false, "Pin Message" is hidden.
  • quoteMessage — When false, "Reply" is hidden.
  • readEvents — When false, read receipts aren't rendered.
  • sendLinks — When false, users can't send URLs.
  • sendMessage — When false, SendMessageDisallowedIndicator replaces the input.
  • sendReaction — When false, the reaction selector is hidden.
  • sendReply — When false, "Thread Reply" is hidden.
  • sendTypingEvents — When false, typing events aren't sent.
  • updateAnyMessage — When false, "Edit Message" is hidden for received messages.
  • updateOwnMessage — When false, "Edit Message" is hidden for own messages.
  • uploadFile — When false, AttachButton is hidden in MessageComposer.

Component Overrides

UI component overrides are no longer passed as props to Channel. Instead, use the WithComponents wrapper to provide custom components. See the Registering Custom Components section above for usage examples.