This is beta documentation for Stream Chat React Native SDK v9. For the latest stable version, see the latest version (v8) .

Upgrading to v9

Version 9 of the Stream Chat React Native SDK is a ground-up redesign of the entire UI layer. Every visual component has been rethought to deliver a modern, polished chat experience out of the box — backed by a new design token system, a slot-based composition architecture for the message input, a centralized state store system, and a brand-new base UI component library.

If you are upgrading from V8, expect to touch theme overrides, component imports, and any code that customizes the message input, attachment picker, channel preview, or overlay system. This guide walks through every breaking change, highlights what replaced what, and ends with a concrete checklist you can follow.


React Native New Architecture Required

V9 supports the React Native New Architecture only. The old architecture (Paper renderer and the legacy bridge) is no longer supported. Before upgrading to V9, ensure that:

  1. Your project is running on React Native 0.76+ (or an Expo SDK version that defaults to the New Architecture).
  2. The New Architecture is enabled in your project configuration.
  3. All other third-party native modules you depend on are New Architecture compatible.

If your project is still on the old architecture, you must migrate to the New Architecture first. Refer to the React Native New Architecture guide for instructions.


New Peer Dependencies

V9 introduces one new required peer dependency and one optional one:

PackageMinimum VersionRequired?Purpose
react-native-teleport>=0.5.4YesPortal-based overlay system for the new message menus
react-native-keyboard-controller>=1.20.2NoSmoother, animation-driven keyboard interactions
# Required
yarn add react-native-teleport

# Optional — install for improved keyboard handling
yarn add react-native-keyboard-controller

react-native-keyboard-controller is entirely optional. The SDK works without it, but when installed it provides smoother keyboard animations and more reliable keyboard height tracking. You can add it at any time.

Expo users: Both packages ship with config plugins. Run npx expo prebuild after installing.


Keyboard Handling Changes

V9 refactors the built-in KeyboardCompatibleView so chat screens work correctly with the current edge-to-edge layout expectations. If you previously carried keyboard workarounds forward from older SDK versions, review them carefully during migration.

Remove Old Android IME Workarounds

If you previously added manual Android IME padding, extra bottom spacing, or similar layout hacks to keep the composer above the keyboard, remove them in V9. The refactored KeyboardCompatibleView should now handle those cases itself.

This also applies if you were following older troubleshooting advice from the V8 docs around incorrect input positioning on Android:

Use disableKeyboardCompatibleView only as an escape hatch if your app already owns keyboard avoidance or if a specific Android window configuration still reports incorrect measurements.

Use Header Height for keyboardVerticalOffset

Set keyboardVerticalOffset to your navigation header height on both Android and iOS. Keep the value consistent anywhere the same chat layout is rendered.

If you previously used Android-only values such as keyboardVerticalOffset={-300} or other artificial negative offsets, remove them in V9. The KeyboardCompatibleView should handle keyboard movement for you, so negative offsets should no longer be necessary.

Do Not Wrap MessageComposer in SafeAreaView

Do not wrap MessageComposer in an extra SafeAreaView just to fix bottom spacing. In V9, the composer handles its own safe-area behavior correctly, and additional wrapping can introduce double-padding or incorrect keyboard positioning.


Dependency Version Bumps

PackageV8 VersionV9 VersionNotes
stream-chat^9.27.2^9.36.0Includes new server-side features (reminders, save-for-later, etc.)
@gorhom/bottom-sheet^5.1.85.1.8Pinned to an exact version to avoid regressions
lodash-es4.17.214.17.23Patch-level bump
linkifyjs^4.3.1^4.3.2Patch-level bump

Design Token System

V8 used ad-hoc color values and scattered style constants. V9 replaces all of this with a layered design token system that makes theming consistent and predictable.

The New theme/ Module

The SDK now exports a theme/ module with four layers:

LayerWhat it Contains
primitivesLow-level building blocks — shadow, radius, spacing, and typography constants (e.g. primitives.spacingSm, primitives.radiusLg).
foundations99 color properties across 10 palettes (blue, cyan, green, lime, neutral, purple, red, slate, violet, yellow) at intensities 50–900, plus 20 layout sizes, 11 radii, 13 spacing values, 6 stroke widths, and 27 typography properties.
components38 component-specific dimension tokens for buttons, composers, devices, icons, and message bubbles.
semantics199 purpose-driven color properties (backgrounds, borders, text, accents, avatars, badges, buttons, chat bubbles, controls, inputs, etc.), shipped in three variants: light, dark, and high-contrast-light.

The defaultTheme object now exposes a top-level semantics property alongside the existing colors:

const theme = useTheme();
const { semantics, colors } = theme;

// Semantic tokens are named by purpose, not by hue
const bgColor = semantics.backgroundElevationElevation1;
const borderColor = semantics.borderCoreDefault;
const textColor = semantics.textPrimary;

Migration tip: Wherever you were using raw colors.* values (e.g. colors.grey, colors.white_snow), prefer the equivalent semantics.* token. Semantic tokens automatically adapt across light, dark, and high-contrast themes.

Color Changes

The colors type no longer includes an index signature ({ [key: string]: string }). If you were storing custom color keys on the colors object, move them to the semantics object or extend the theme type.

Semantic Token Renames

If your app reads semantic tokens directly, note these contract-level renames introduced by the recent token update:

Previous TokenNew Token
backgroundCoreSurfacebackgroundCoreSurfaceDefault
badgeTextInversebadgeTextOnInverse
textInversetextOnInverse
backgroundCoreElevation4Removed with no direct replacement

The token update also added new semantic tokens such as borderCoreOnInverse, borderCoreOnSurface, borderUtilityDisabledOnSurface, controlChipBorder, and controlChipText.

Default Value Changes

PropertyV8 ValueV9 ValueNotes
screenPadding816Doubled for better visual spacing
BASE_AVATAR_SIZE3224Reduced to match new UI density
loadingDots.spacing4primitives.spacingXxsNow references a design token

New Base UI Component Library

V9 introduces a ui/ component library that provides standardized, token-driven UI primitives. These replace the older ad-hoc components and give you building blocks that are consistent with the SDK's new design language.

Avatar

The Avatar and GroupAvatar components have been moved from components/Avatar/ to components/ui/Avatar/. The old exports are removed.

V8:

import { Avatar, GroupAvatar } from "stream-chat-react-native";
// Props: image, name, online, presenceIndicator, size (number)

V9:

import { Avatar } from "stream-chat-react-native";
// Props: size ('2xl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs'),
//        imageUrl, placeholder, showBorder, backgroundColor, style
ChangeDetails
sizeNow a string enum ('xs''2xl') instead of a pixel number
imageimageUrlRenamed for clarity
name removedUse placeholder for fallback content
online / presenceIndicatorRemoved — use the separate OnlineIndicator badge component instead
ChannelAvatarMoved to ui/Avatar/ChannelAvatar; now uses size='xl' by default
GroupAvatarReplaced by AvatarGroup and AvatarStack in the new library

Button

A new standardized Button component with variant, type, and size props:

import { Button } from "stream-chat-react-native";

<Button
  variant="primary" // 'primary' | 'secondary' | 'destructive'
  type="solid" // 'solid' | 'outline' | 'ghost' | 'liquidGlass'
  size="md" // 'sm' | 'md' | 'lg'
  LeadingIcon={SendRight}
  label="Send"
  disabled={false}
/>;

Badge Components

ComponentPurpose
BadgeNotificationUnread counts and notification badges
OnlineIndicatorUser presence status (replaces Avatar's online prop)
ErrorBadgeError state indicators
GiphyBadgeGiphy source badge on media
ImgurBadgeImgur source badge on media
BadgeCountGeneric numeric badge

Other New Primitives

ComponentPurpose
InputStandardized text input component
VideoPlayIndicatorPlay button overlay with size variants ('sm' / 'md' / 'lg')
SpeedSettingsButtonPlayback speed control for audio/video
GiphyChipGiphy indicator chip for message input

MessageComposer — Complete Redesign

The message composer underwent the most significant transformation in V9. It moves from a flat, prop-heavy component to a slot-based composition architecture that is easier to customize and extend.

Architecture Change

V8: A single component with many inline conditional renders. Buttons (Send, AudioRecord, Cooldown, Commands, MoreOptions) were rendered directly in the component body and customized via individual component props.

V9: A slotted layout with clearly defined regions that you can replace independently:

MessageComposer
├── MessageComposerLeadingView    ← wraps InputButtons (attach, etc.)
├── Main Input Area
│   ├── MessageInputHeaderView    ← reply preview, edit header, attachments, link previews
│   ├── AudioRecordingPreview / AudioRecordingInProgress / InputView
├── MessageInputTrailingView      ← wraps OutputButtons (send, edit, cooldown, audio)
├── MessageComposerTrailingView   ← empty slot for your own custom content
├── AutoCompleteSuggestionList
└── Poll Creation Modal

New useMessageComposer Hook

V9 exposes a useMessageComposer() hook that gives you direct access to the composer's internal state without going through context:

const messageComposer = useMessageComposer();
const { attachmentManager, textComposer, editedMessage, channel, hasCommands } =
  messageComposer;

New Slot Props

These composition slot components can be passed to the Channel component to customize regions of the input:

PropDescription
MessageComposerLeadingViewLeft side of the composer — wraps InputButtons
MessageComposerTrailingViewRight side of the composer — empty slot for custom trailing content
MessageInputHeaderViewHeader area — consolidates reply preview, edit header, attachments, and link previews
MessageInputFooterViewFooter area below the input — empty slot for custom content
MessageInputLeadingViewLeading area of the input text row
MessageInputTrailingViewTrailing area — wraps OutputButtons (Send / Edit / Cooldown / Audio)
InputViewThe text input view itself
messageInputFloatingboolean — enables a floating input layout (default: false)

Removed Props

The following props are removed from Channel and MessageInputContext:

Removed PropWhat to Use Instead
CommandsButtonCommands are handled internally via the new CommandChip component
MoreOptionsButtonConsolidated into InputButtons
InputEditingStateHeaderConsolidated into MessageInputHeaderView
InputReplyStateHeaderConsolidated into MessageInputHeaderView
CommandInputReplaced by the internal CommandChip component
AttachmentPickerBottomSheetHandleRemoved — the attachment picker has been fully redesigned
attachmentPickerBottomSheetHandleHeightRemoved
CameraSelectorIconReplaced by AttachmentTypePickerButton
FileSelectorIconReplaced by AttachmentTypePickerButton
ImageSelectorIconReplaced by AttachmentTypePickerButton
VideoRecorderSelectorIconReplaced by AttachmentTypePickerButton
CreatePollIconPoll creation is handled internally
AttachmentPickerErrorRemoved
AttachmentPickerErrorImageRemoved
AttachmentUploadProgressIndicatorSplit into six granular indicators (see Upload Indicators)
cooldownEndsAtCooldown is now managed internally by OutputButtons
selectedPickerRemoved from context — use useAttachmentPickerState() hook
toggleAttachmentPickerReplaced by openAttachmentPicker / closeAttachmentPicker

New Upload Indicator Components

The single AttachmentUploadProgressIndicator is replaced by six specific components, giving you finer control over each upload state:

ComponentPurpose
FileUploadInProgressIndicatorProgress indicator for file uploads
FileUploadRetryIndicatorRetry button for failed file uploads
FileUploadNotSupportedIndicatorIndicator for unsupported file types
ImageUploadInProgressIndicatorProgress indicator for image uploads
ImageUploadRetryIndicatorRetry button for failed image uploads
ImageUploadNotSupportedIndicatorIndicator for unsupported images

OutputButtons Component

A new OutputButtons component manages the trailing action buttons using priority-based rendering with animated transitions (ZoomIn/ZoomOut, 200ms):

PriorityConditionButton Shown
1AI streaming is activeStopMessageStreamingButton
2User is editing a message or commandEditButton
3Slow-mode cooldown is activeCooldownTimer
4Audio recording enabled, no text/attachmentsStartAudioRecordingButton
5DefaultSendButton

Component Path Changes

V8 PathV9 Path
MessageInput/AttachButtonMessageInput/components/InputButtons/AttachButton
MessageInput/SendButtonMessageInput/components/OutputButtons/SendButton
MessageInput/CooldownTimerMessageInput/components/OutputButtons/CooldownTimer
MessageInput/InputButtonsMessageInput/components/InputButtons
MessageInput/AttachmentUploadPreviewListMessageInput/components/AttachmentPreview/AttachmentUploadPreviewList

New MessageComposer Components

ComponentDescription
CommandChipDisplays the active slash command with a dismiss button
EditButtonConfirm button shown when editing a message
LinkPreviewListRenders URL preview cards inline in the input area
MicPositionContextContext provider for audio recording microphone position

MessageInputContext Changes

Removed Values

Removed ValueWhat to Use Instead
asyncMessagesMultiSendEnabledaudioRecordingSendOnComplete with inverted semantics
cooldownEndsAtManaged internally in OutputButtons
selectedPickeruseAttachmentPickerState() hook
toggleAttachmentPickeropenAttachmentPicker / closeAttachmentPicker
getMessagesGroupStylesRemoved — grouping logic is now internal
legacyImageViewerSwipeBehaviourRemoved — the legacy image viewer is no longer supported

Added Values

New ValueTypeDescription
audioRecordingSendOnCompletebooleanWhether a completed voice recording is sent immediately
audioRecorderManagerAudioRecorderManagerManages audio recording lifecycle and state
startVoiceRecording() => Promise<boolean>Start a voice recording
stopVoiceRecording() => Promise<void>Stop the current voice recording
deleteVoiceRecording() => Promise<void>Delete the current voice recording
uploadVoiceRecording(sendOnComplete: boolean) => Promise<void>Upload the recorded voice message
messageInputFloatingbooleanWhether the input is using floating layout
messageInputHeightStoreMessageInputHeightStoreState store for tracking input height
createPollOptionGapnumberGap between poll options in creation UI

Modified Values

ValueV8 TypeV9 Type
takeAndUploadImage() => Promise<void>() => Promise<{askToOpenSettings?; canceled?}>
CooldownTimerComponentType<CooldownTimerProps>ComponentType (no props — self-contained)
AudioRecordingPreviewComponentType<AudioRecordingPreviewProps>ComponentType (no props — self-contained)
VideoAttachmentUploadPreviewComponentType<FileAttachmentUploadPreviewProps>ComponentType<VideoAttachmentUploadPreviewProps>

Voice recording send behavior

asyncMessagesMultiSendEnabled has been removed and replaced with audioRecordingSendOnComplete on Channel, MessageComposer, and useMessageInputContext().

This is not a straight rename; the meaning is inverted:

  • V8: asyncMessagesMultiSendEnabled={true} meant recordings were not sent immediately and instead stayed in the composer.
  • V9: audioRecordingSendOnComplete={true} means recordings are sent immediately after upload.
  • Set audioRecordingSendOnComplete={false} when you want recordings to stay in the composer so users can add text, attachments, or more recordings before sending.

The default behavior also changed:

  • Before: asyncMessagesMultiSendEnabled defaulted to true, so recordings did not send immediately.
  • Now: audioRecordingSendOnComplete defaults to true, so recordings do send immediately.

Advanced integrations should also update any direct uploadVoiceRecording(...) calls from useMessageInputContext(). The callback now takes sendOnComplete semantics instead of multiSendEnabled semantics.


AudioAttachment Changes

The audio playback system has been rewritten to use a centralized player model.

Removed Props

Removed PropNotes
onLoadReplaced by the useAudioPlayer hook
onPlayPauseReplaced by the useAudioPlayer hook
onProgressReplaced by the useAudioPlayer hook
titleMaxLengthRemoved — use showTitle boolean instead

Added Props

New PropTypeDescription
showTitlebooleanControls whether the audio title is shown
containerStyleViewStyleStyle override for the container
indicatorReactElementCustom indicator element
stylesobjectGranular style overrides

Hook Rename

V8 HookV9 Hook
useAudioControlleruseAudioRecorder
useAudioPlayerControluseAudioPlayer

MessageList Changes

New Props

PropTypeDescription
animateLayoutbooleanEnables layout animations for message insertion/removal (default: true)
messageInputFloatingbooleanTells the list whether the input is in floating mode
messageInputHeightStorestate storeState store for coordinating with the input's height
InlineDateSeparatorComponentCustomizable inline date separator rendered between messages
InlineUnreadIndicatorComponentCustomizable inline unread indicator rendered at the unread boundary
MessageComponentOverride the default message component
attachmentPickerStorestate storeState store for the attachment picker

Removed Props

PropNotes
isListActiveRemoved
legacyImageViewerSwipeBehaviourRemoved — the legacy image viewer is no longer supported
setMessagesRemoved — use the centralized state store instead
selectedPickerReplaced by attachmentPickerStore
setSelectedPickerReplaced by attachmentPickerStore

Modified Props

PropV8 TypeV9 Type
additionalFlatListPropsPartial<FlatListProps<LocalMessage>>Partial<FlatListProps<MessageListItemWithNeighbours>>
setFlatListRefFlatListType<LocalMessage>FlatListType<MessageListItemWithNeighbours>
channelUnreadStateDirect state valueReplaced by channelUnreadStateStore

The FlatList item type changed from LocalMessage to MessageListItemWithNeighbours. Each item now carries references to its preceding and following messages, enabling smarter grouping and rendering.

New Sub-Components

ComponentDescription
ScrollToBottomButtonRedesigned with animated entrance — uses the new Button and BadgeNotification components
StickyHeaderSticky date header that tracks the scroll position
UnreadMessagesNotificationRedesigned with a two-button layout: "Jump to unread" and dismiss

MessagesContext Changes

Removed Values

Removed ValueWhat to Use Instead
AttachmentActionsRemoved — actions are now inline on attachments
CardReplaced by UrlPreview with new URLPreviewProps
CardCover / CardFooter / CardHeaderRemoved — the card component is redesigned as UrlPreview
ImageReloadIndicatorRemoved
MessageEditedTimestampFolded into the message footer
getMessagesGroupStylesRemoved — grouping is handled internally
legacyImageViewerSwipeBehaviourRemoved

Added Values

New ValueTypeDescription
urlPreviewType'compact' | 'full'Controls URL preview style (compact card vs. full embed)
URLPreviewCompactComponentType<URLPreviewCompactProps>Compact URL preview component
FilePreviewComponentType<FilePreviewProps>Standalone file preview component
MessageReminderHeaderComponentType<MessageReminderHeaderProps>Header shown on reminded messages
MessageSavedForLaterHeaderComponentType<MessageSavedForLaterHeaderProps>Header shown on saved-for-later messages
SentToChannelHeaderComponentType<SentToChannelHeaderProps>Header for thread replies that were also sent to the channel
handleBlockUser(user) => Promise<void>Handler for blocking a user from the message menu
reactionListType'clustered' | 'segmented'Controls reaction list rendering style
ReactionListClusteredComponentClustered reaction list display
ReactionListItemComponentIndividual reaction item
ReactionListItemWrapperComponentWrapper for reaction items
ReactionListCountItemComponentReaction count display
UnsupportedAttachmentComponentType<UnsupportedAttachmentProps>Fallback renderer for unsupported/custom attachment types

Modified Values

ValueV8 TypeV9 Type
UrlPreviewComponentType<CardProps>ComponentType<URLPreviewProps>
MessageItemViewComponentType<MessageSimpleProps>ComponentType<MessageItemViewProps>
MessageHeaderOptional ComponentType<MessageFooterProps>Required ComponentType<MessageHeaderProps>

Note: MessageHeader is now a required value in the context. If you were relying on it being optional, make sure your custom provider supplies a component.

MessageSimpleMessageItemView

MessageSimple has been renamed to MessageItemView. This is a breaking rename across the component name, exported prop types, Channel override, MessagesContext slot, source path, and theme namespace.

  • MessageSimpleMessageItemView
  • MessageSimplePropsMessageItemViewProps
  • MessageSimplePropsWithContextMessageItemViewPropsWithContext
  • <Channel MessageSimple={...} /><Channel MessageItemView={...} />
  • messagesContext.MessageSimplemessagesContext.MessageItemView
  • messageSimple theme key → messageItemView
  • message-simple-wrapper test ID → message-item-view-wrapper
  • Message/MessageSimple/...Message/MessageItemView/...

Before:

<Channel MessageSimple={CustomMessageSimple} />;

import { MessageSimple } from "stream-chat-react-native";

const theme = {
  messageSimple: {
    content: {
      container: {},
    },
  },
};

After:

<Channel MessageItemView={CustomMessageItemView} />;

import { MessageItemView } from "stream-chat-react-native";

const theme = {
  messageItemView: {
    content: {
      container: {},
    },
  },
};

If your old MessageSimple override only added spacing or lightweight decoration around the message body, prefer the new MessageSpacer, MessageContentTopView, MessageContentBottomView, MessageContentLeadingView, and MessageContentTrailingView slots over replacing MessageItemView wholesale. These are optional override slots, not new built-in default components.

MessageAvatarMessageAuthor

MessageAvatar has been renamed to MessageAuthor. This is a breaking rename across the component name, prop types, Channel override, MessagesContext slot, source path, and the messageItemView.authorWrapper theme surface.

  • MessageAvatarMessageAuthor
  • MessageAvatarPropsMessageAuthorProps
  • MessageAvatarPropsWithContextMessageAuthorPropsWithContext
  • <Channel MessageAvatar={...} /><Channel MessageAuthor={...} />
  • messagesContext.MessageAvatarmessagesContext.MessageAuthor
  • messageItemView.avatarWrappermessageItemView.authorWrapper
  • message-avatar test ID → message-author
  • Message/MessageSimple/MessageAvatarMessage/MessageItemView/MessageAuthor

Before:

import { MessageAvatar } from "stream-chat-react-native";

<Channel MessageAvatar={CustomMessageAvatar} />;

const theme = {
  messageItemView: {
    avatarWrapper: {
      container: {},
    },
  },
};

After:

import { MessageAuthor } from "stream-chat-react-native";

<Channel MessageAuthor={CustomMessageAuthor} />;

const theme = {
  messageItemView: {
    authorWrapper: {
      container: {},
    },
  },
};

MessageInputMessageComposer

MessageInput has been renamed to MessageComposer. If you were rendering the input directly inside Channel, importing its prop types, customizing the thread input through Thread, or overriding its theme namespace, update those references to MessageComposer.

  • MessageInputMessageComposer
  • MessageInputPropsMessageComposerProps
  • <MessageInput /><MessageComposer />
  • import { MessageInput } from 'stream-chat-react-native'import { MessageComposer } from 'stream-chat-react-native'
  • theme.messageInputtheme.messageComposer
  • Thread.MessageInputThread.MessageComposer
  • additionalMessageInputPropsadditionalMessageComposerProps
  • MessageInput/MessageInputMessageComposer/MessageComposer

Before:

import { MessageInput } from "stream-chat-react-native";

const theme = {
  messageInput: {
    sendButton: {},
  },
};

<Channel>
  <MessageInput />
</Channel>;

<Thread
  MessageInput={CustomMessageInput}
  additionalMessageInputProps={{ autoFocus: true }}
/>;

After:

import { MessageComposer } from "stream-chat-react-native";

const theme = {
  messageComposer: {
    sendButton: {},
  },
};

<Channel>
  <MessageComposer />
</Channel>;

<Thread
  MessageComposer={CustomMessageComposer}
  additionalMessageComposerProps={{ autoFocus: true }}
/>;

This rename does not include MessageInputContext, the MessageInput folder name, or helper component names such as MessageInputHeaderView, MessageInputFooterView, MessageInputLeadingView, and MessageInputTrailingView.

ChannelListMessengerChannelListView

ChannelListMessenger has been renamed to ChannelListView. If you imported the default channel list UI directly, referenced its prop types, or customized theme styles under channelListMessenger, update those references to ChannelListView and channelListView.

  • ChannelListMessengerChannelListView
  • ChannelListMessengerPropsChannelListViewProps
  • ChannelListMessengerPropsWithContextChannelListViewPropsWithContext
  • import { ChannelListMessenger } from 'stream-chat-react-native'import { ChannelListView } from 'stream-chat-react-native'
  • theme.channelListMessengertheme.channelListView
  • channel-list-messenger test ID → channel-list-view
  • ChannelList/ChannelListMessengerChannelList/ChannelListView

Before:

import { ChannelListMessenger } from "stream-chat-react-native";

const theme = {
  channelListMessenger: {
    flatList: {},
    flatListContent: {},
  },
};

<ChannelList List={ChannelListMessenger} />;

After:

import { ChannelListView } from "stream-chat-react-native";

const theme = {
  channelListView: {
    flatList: {},
    flatListContent: {},
  },
};

<ChannelList List={ChannelListView} />;

ChannelPreviewMessengerChannelPreviewView

ChannelPreviewMessenger has been renamed to ChannelPreviewView. If you imported the default channel preview row directly, referenced its prop types, or passed it into ChannelList as a custom preview component, update those references to ChannelPreviewView. This also affects the typed Preview slot in ChannelsContext. The source path has moved from ChannelPreview/ChannelPreviewMessenger to ChannelPreview/ChannelPreviewView.

  • ChannelPreviewMessengerChannelPreviewView
  • ChannelPreviewMessengerPropsChannelPreviewViewProps
  • ChannelPreviewMessengerPropsWithContextChannelPreviewViewPropsWithContext
  • import { ChannelPreviewMessenger } from 'stream-chat-react-native'import { ChannelPreviewView } from 'stream-chat-react-native'
  • <ChannelList Preview={ChannelPreviewMessenger} /><ChannelList Preview={ChannelPreviewView} />
  • ChannelPreview/ChannelPreviewMessengerChannelPreview/ChannelPreviewView

Before:

import { ChannelPreviewMessenger } from "stream-chat-react-native";

<ChannelList Preview={ChannelPreviewMessenger} />;

After:

import { ChannelPreviewView } from "stream-chat-react-native";

<ChannelList Preview={ChannelPreviewView} />;

The channelPreview theme namespace remains unchanged. Subcomponents such as ChannelPreviewStatus, ChannelPreviewTitle, and ChannelPreviewMessage were not renamed in this pass, and the existing channel-preview-button test ID is unchanged.

MessageHeader redesign

In v8, MessageSimple passed a richer runtime prop bag into custom headers, including alignment, date, isDeleted, lastGroupMessage, members, message, MessageStatus, otherAttachments, and showMessageStatus.

In v9, the public prop surface is limited to message?, and the default MessageHeader reads its data and subcomponents from useMessageContext() and useMessagesContext() instead. If you customized MessageHeader, move that logic into hooks rather than relying on the old explicit props.

V8 PropV9 Source
alignmentuseMessageContext()
messageuseMessageContext() or optional message prop
datemessage.created_at
isDeletedmessage.type === 'deleted'
lastGroupMessageuseMessageContext()
membersuseMessageContext()
MessageStatususeMessagesContext()
otherAttachmentsuseMessageContext()
showMessageStatususeMessageContext()

If your old customization was only about pinned, reminder, saved-for-later, or sent-to-channel header UI, prefer overriding MessagePinnedHeader, MessageReminderHeader, MessageSavedForLaterHeader, or SentToChannelHeader directly.

Swipe-to-reply boundary and width prop removal

MessageBubble is no longer the swipe-to-reply boundary. Swipe-to-reply now wraps the full MessageItemView, so the entire rendered message row participates in the swipe interaction instead of only the inner bubble.

The swipe gesture behavior itself is otherwise unchanged:

  • The same messageSwipeToReplyHitSlop handling still applies.
  • Horizontal activation, thresholds, haptics, MessageSwipeContent, and spring-back behavior are unchanged.
  • Deleted messages are still not swipeable.
  • When enableSwipeToReply is false, no swipe wrapper is mounted.

In addition, messageContentWidth and setMessageContentWidth have been removed from the message rendering stack. If you were using custom MessageContent, MessageBubble, or ReactionListTop implementations that referenced these props, remove that logic and any related test mocks.

  • MessageBubble is no longer the swipe boundary; MessageItemView is.
  • messageContentWidth was removed from ReactionListTop.
  • setMessageContentWidth was removed from MessageContent.
  • The underlying MessageBubble / MessageItemView chain no longer passes width-tracking state through.

Before:

const CustomMessageContent = (props) => {
  props.setMessageContentWidth?.(100);
  return <MessageContent {...props} />;
};

After:

const CustomMessageContent = (props) => {
  return <MessageContent {...props} />;
};

Channel Component Changes

New Props

PropTypeDefaultDescription
messageInputFloatingbooleanfalseEnables the floating message input layout
handleBlockUserfunctionCallback for blocking a user
reactionListType'clustered' | 'default''clustered'Switches between clustered and default reaction lists
urlPreviewType'full' | 'compact''full'Controls URL preview display style
onAlsoSentToChannelHeaderPressfunctionPress handler for thread "also sent to channel" header
asyncMessagesLockDistancenumber50Audio recording gesture: lock distance threshold
asyncMessagesMinimumPressDurationnumber500Audio recording gesture: minimum press duration (ms)
audioRecordingSendOnCompletebooleantrueWhether a completed voice recording is sent immediately
asyncMessagesSlideToCancelDistancenumber75Audio recording gesture: slide-to-cancel distance
createPollOptionGapnumberGap between poll options in the creation UI
MessageReminderHeaderComponentCustom header for message reminders
MessageSavedForLaterHeaderComponentCustom header for saved messages
SentToChannelHeaderComponentCustom header for "also sent to channel" thread messages
ReactionListClusteredComponentCustom clustered reaction list
ReactionListCountItemComponentCustom reaction count item
ReactionListItemComponentCustom reaction list item
ReactionListItemWrapperComponentCustom reaction list item wrapper
URLPreviewCompactComponentCustom compact URL preview
FilePreviewComponentCustom file preview component
UnsupportedAttachmentComponentFallback renderer for unsupported/custom attachment types

All the MessageComposer*View and MessageInput*View slot props from the MessageComposer section are also available on Channel.

Removed Props

Removed PropNotes
legacyImageViewerSwipeBehaviourLegacy image viewer is removed
isAttachmentEqualRemoved
MessageEditedTimestampFolded into the message footer
ImageReloadIndicatorRemoved
AttachmentActionsRemoved — actions are inline on attachments
Card / CardCover / CardFooter / CardHeaderReplaced by UrlPreview / URLPreviewCompact
AttachmentPickerIOSSelectMorePhotosRemoved
All attachment picker selector iconsSee MessageComposer — Removed Props

Modified Default Values

PropV8 DefaultV9 DefaultImpact
messageContentOrder['quoted_reply', 'gallery', 'files', 'poll', 'ai_text', 'text', 'attachments', 'location']['quoted_reply', 'gallery', 'files', 'poll', 'ai_text', 'attachments', 'text', 'location']'attachments' and 'text' are swapped
numberOfAttachmentImagesToLoadPerCall6025Reduced for better performance on lower-end devices
attachmentPickerBottomSheetHeightvh(45) (viewport-based)disableAttachmentPicker ? 72 : 333 (fixed)Switched from viewport-relative to fixed pixel values
attachmentSelectionBarHeight5272Increased for better touch targets

Action required: If your UI depends on 'text' rendering before 'attachments', explicitly pass the V8 order array to messageContentOrder.


ChannelPreview Changes

The default preview row has been renamed to ChannelPreviewView, and the channel preview components have been redesigned with new hooks and a restructured layout.

Key API Change: latestMessagePreviewlastMessage

In V8, ChannelPreview exposed a latestMessagePreview object with pre-formatted preview data. In V9, this is replaced with lastMessage — the raw last message object — and specialized hooks for formatting:

// V8
const { latestMessagePreview } = useChannelPreviewContext();
const previewText = latestMessagePreview?.messageObject?.text;

// V9
const { lastMessage } = useChannelPreviewContext();
const previewText = lastMessage?.text;

New Components

ComponentDescription
ChannelDetailsBottomSheetBottom sheet overlay for channel details and actions
ChannelLastMessagePreviewFormatted last message preview with attachment/poll awareness
ChannelMessagePreviewDeliveryStatusDelivery status indicator in the channel preview
ChannelPreviewMutedStatusMuted state indicator with configurable position
ChannelPreviewTypingIndicatorTyping indicator shown in the channel preview

Removed Components

Removed ComponentNotes
ChannelAvatarReplaced by ChannelAvatar from ui/Avatar/ (different API)
PreviewAvatarRemoved from context — use ChannelAvatar with size='xl'

New Props

PropTypeDescription
mutedStatusPosition'inlineTitle' | 'trailingBottom'Where the muted icon appears in the channel preview

New Hooks

HookDescription
useChannelPreviewDraftMessageAccess draft message data for a channel
useChannelPreviewPollLabelGet a formatted label for the last poll
useChannelTypingStateObserve who is currently typing in the channel

Hook Export Changes

Individual hook exports from ChannelPreview/hooks/, ChannelList/hooks/, and Chat/hooks/ are consolidated into barrel exports. The hooks themselves are still available, but you import from the barrel:

// V8
import { useChannelPreviewDisplayName } from "stream-chat-react-native/ChannelPreview/hooks/useChannelPreviewDisplayName";

// V9
import { useChannelPreviewDisplayName } from "stream-chat-react-native";

Hook Rename

V8 HookV9 Hook
useMutedUsers (from Chat)useClientMutedUsers

Attachment Components

Renamed Components

V8 ComponentV9 ComponentNotes
AudioAttachmentAudioRenamed; file moved to Audio/ subdirectory
CardUrlPreviewRenamed with new URLPreviewProps interface

Removed Components

Removed ComponentNotes
AttachmentActionsRemoved — actions are now handled inline on attachments
ImageReloadIndicatorRemoved
AttachmentUnsupportedIndicatorRemoved

New Components

ComponentDescription
URLPreviewCompactCompact URL preview with card-style layout
UnsupportedAttachmentReplaces the old unsupported indicator with a full component

AttachmentPicker Overhaul

The attachment picker has been completely redesigned with a simpler, more extensible architecture.

Removed:

  • AttachmentPickerBottomSheetHandle
  • AttachmentPickerError / AttachmentPickerErrorImage
  • CameraSelectorIcon / FileSelectorIcon / ImageSelectorIcon / VideoRecorderSelectorIcon
  • AttachmentPickerIOSSelectMorePhotos
  • AttachmentPickerItem

Added:

ComponentDescription
AttachmentPickerContentMain content area of the redesigned picker
AttachmentMediaPickerMedia selection UI (photos and videos)
AttachmentTypePickerButtonUnified type selector button replacing all individual icon components

OverlayProvider Changes

The overlay system has been rearchitected to use a portal-based approach powered by react-native-teleport. This eliminates the need for complex z-index management and provides smoother overlay animations.

New Architecture

The OverlayProvider now wraps its children with a PortalProvider and renders a MessageOverlayHostLayer for the new portal-based message overlay. This means message context menus and reaction pickers render in a true overlay portal rather than within the component tree.

New Props

PropTypeDescription
MessageOverlayBackgroundReact.ComponentTypeCustom background component for the message overlay

Removed Props

Removed PropWhat to Use Instead
imageGalleryGridHandleHeightRemoved
imageGalleryGridSnapPointsRemoved
imageGalleryCustomComponentsReplaced by direct component props (see below)

The nested imageGalleryCustomComponents object is replaced by direct component props for simpler customization:

V8:

<OverlayProvider
  imageGalleryCustomComponents={{
    footer: { Component: CustomFooter },
    header: { Component: CustomHeader },
    grid: { Component: CustomGrid },
    gridHandle: { Component: CustomGridHandle },
  }}
>

V9:

<OverlayProvider
  ImageGalleryHeader={CustomHeader}
  ImageGalleryFooter={CustomFooter}
  ImageGalleryVideoControls={CustomVideoControls}
  ImageGalleryGrid={CustomGrid}
>

New Context Value

OverlayContextValue now includes overlayOpacity (SharedValue<number>) — a Reanimated shared value you can use to drive custom overlay animations.


ImageGallery Changes

The image gallery has been refactored to use a centralized state store instead of in-component state.

State Store

A new ImageGalleryStateStore replaces in-component state management:

  • ImageGalleryAsset type replaces the old Photo type.
  • Gallery configuration (autoPlayVideo, giphyVersion) moves from props to ImageGalleryOptions in the state store.
  • Three utility functions are exported: isViewableImageAttachment, isViewableVideoAttachment, isViewableGiphyAttachment.
// V8
<Gallery autoPlayVideo={true} giphyVersion="original" />;

// V9
const { imageGalleryStateStore } = useImageGalleryContext();
imageGalleryStateStore.openImageGallery(/* ... */);

Removed

RemovedNotes
imageGalleryCustomComponentsReplaced by direct component props
autoPlayVideo (as prop)Moved to ImageGalleryOptions in state store
giphyVersion (as prop)Moved to ImageGalleryOptions in state store
imageGalleryGridSnapPointsRemoved
imageGalleryGridHandleHeightRemoved
ImageGalleryOverlayRemoved
ImageGridHandleRemoved
legacyImageViewerSwipeBehaviourRemoved
setMessages / setSelectedMessageUse imageGalleryStateStore instead

Added

AddedDescription
ImageGalleryVideoControlsComponent prop for video playback controls
ImageGalleryVideoControlVideo controls UI component
useImageGalleryVideoPlayerHook for managing video player state

Message Components

New Components

ComponentDescription
MessageHeaderConsolidated message header — now required in MessagesContext
MessageBlockedUI for blocked messages
MessageSwipeContentContent revealed on message swipe
MessageTextContainerWrapper for message text with consistent styling
ReactionListClusteredClustered reaction list display (new default style)
ReactionListItemIndividual reaction item component
ReactionListItemWrapperWrapper for reaction items

Removed Components

Removed ComponentNotes
MessageEditedTimestampFolded into the message footer component
MessagePreviewReplaced by specialized hooks (see below)

MessagePinnedHeader still exists in v9. It is now rendered as part of the consolidated MessageHeader flow alongside reminder, saved-for-later, and sent-to-channel headers.

MessagePreview → Hooks

The MessagePreview component is removed in favor of composable hooks:

HookDescription
useMessageDeliveryStatusReturns delivery status data for a message preview
useGroupedAttachmentsReturns grouped attachment data for a message
useMessagePreviewIconDetermines the appropriate icon for a preview
useMessagePreviewTextFormats the preview text for a message

State Store Additions

V9 adds four new centralized state store modules that follow the same observable pattern used elsewhere in the SDK:

VideoPlayer

Mirrors the existing audio player architecture for video:

  • VideoPlayer class with VideoPlayerState, VideoDescriptor, VideoPlayerOptions types
  • DEFAULT_PLAYBACK_RATES and INITIAL_VIDEO_PLAYER_STATE constants

VideoPlayerPool

Pool management for multiple concurrent video players:

  • VideoPlayerPool class with VideoPlayerPoolState type

ImageGalleryStateStore

Centralized state for the image gallery (replaces in-component state):

  • ImageGalleryStateStore class
  • ImageGalleryAsset, ImageGalleryState, ImageGalleryOptions types
  • isViewableImageAttachment, isViewableVideoAttachment, isViewableGiphyAttachment utilities

MessageOverlayStore

Complete state management for the portal-based overlay system:

  • Store instances: overlayStore, closingPortalLayoutsStore
  • Lifecycle functions: openOverlay, closeOverlay, scheduleActionOnClose, finalizeCloseOverlay
  • Layout management: setOverlayMessageH, setOverlayTopH, setOverlayBottomH, bumpOverlayLayoutRevision
  • Portal management: createClosingPortalLayoutRegistrationId, setClosingPortalLayout, clearClosingPortalLayout
  • Hooks: useOverlayController, useIsOverlayClosing, useIsOverlayActive, useClosingPortalHostBlacklistState, useShouldTeleportToClosingPortal, useClosingPortalHostBlacklist, useClosingPortalLayouts

New Hooks

HookDescription
useAttachmentPickerStateManages attachment picker open/closed state (replaces context-based approach)
useAudioRecorderAudio recording lifecycle (replaces useAudioController)
useAudioPlayerAudio playback control (replaces useAudioPlayerControl)
useMessageDeliveryStatusMessage delivery status for previews
useGroupedAttachmentsGrouped attachment data for message previews
useMessagePreviewIconIcon selection logic for message previews
useMessagePreviewTextText formatting for message previews
useImageGalleryVideoPlayerVideo player state within the image gallery
useMessageComposerDirect access to message composer state and methods

Renamed Hooks

V8 HookV9 Hook
useAudioControlleruseAudioRecorder
useAudioPlayerControluseAudioPlayer
useMutedUsersuseClientMutedUsers

New Contexts

ContextDescription
BottomSheetContextProvides bottom sheet control (close() method)

Icons Changes

V9 significantly overhauls the icon set. In addition to the visual refresh, some internal icon source files were renamed to match the current design export asset filenames. Those filename changes are only breaking if you were importing private paths such as package/src/icons/*.

For public SDK consumers, the important changes are:

  • some icon component names were removed entirely
  • some exports kept the same component name but now render different path data
  • several new icon component names were added
  • a few legacy names were explicitly renamed rather than removed

If you were importing icon files directly from package/src/icons/*, there is an additional breaking change in V9:

Private File ImportReplacementNotes
chevron-downchevron-upThe private chevron-down.tsx file was removed
ChevronTopChevronUpRename the directly imported component if you were using that private module

chevron-down.tsx was not part of the public barrel export, and chevron-up.tsx is also not barrel-exported, so this only affects direct file imports into internal icon paths.

Key Renames and Direct Replacements

V8 IconV9 Icon / GuidanceNotes
CircleStopStopConsolidated into the Stop export
RefreshReloadUse Reload instead of Refresh
ArrowRightNo SDK replacementProvide your own local icon if you still need this glyph
CloseNo SDK replacementProvide your own local icon if you still need this glyph
MessageIconNo SDK replacementProvide your own local icon if you still need this glyph
UserNo SDK replacementProvide your own local icon if you still need this glyph
Legacy file-type exportsNew file-type icon familyLegacy file-type exports were consolidated into new V9 icons

New Icons

IconDescription
BlockUserBlock user action
PlusAdd/create action
MinusRemove action
ArrowShareLeftShare/forward action
BellNotification/reminder bell
BookmarkSave for later / bookmark
CheckmarkConfirmation checkmark
CommandsIconSlash commands
FilePickerIconFile picker trigger

Additional new public icon exports in V9 include:

ArrowBoxLeft, ArrowUpRight, ChevronLeft, CircleBan, CircleMinus, Code, DotGrid, Exclamation, ExclamationCircle, EyeOpen, File, Giphy, ImageGrid, InfoTooltip, MapPin, MoreEmojis, OtherFileIcon, PeopleIcon, PhotoIcon, PollIcon, Presentation, ReplyConnectorLeft, ReplyConnectorRight, SpreadSheet, Tick, Unlock, and VideoIcon.

Icons With The Same Component Name but Updated Artwork

These exports still exist in V9, but the underlying icon artwork changed. Review them if you rely on screenshots, snapshots, or pixel-precise custom layouts:

Archive, ArrowUp, Audio, Camera, Check, CheckAll, Copy, CurveLineLeftUp, DOC, Delete, Down, Edit, Flag, GiphyIcon, Imgur, Lightning, Link, Loading, Lock, MenuPointHorizontal, MessageBubbleEmpty, MessageFlag, Mic, Mute, PDF, Pause, Picture, Pin, Play, PollThumbnail, Recorder, Reload, Resend, Search, SendRight, Share, Smile, Sound, Stop, ThreadReply, Time, Unpin, UnreadIndicator, UserAdd, UserDelete, Video, Warning, and ZIP.

If you were using private icon file imports rather than public SDK exports, note these additional visual-only changes:

  • NewClose now renders the tighter xmark-small glyph, even though the component name is unchanged.
  • CommandsIcon, File, Link, Pause, Plus, Stop, and ThreadReply were redrawn to match the latest icon export set without changing their import names.

Removed Icons

If you were importing any of these icons directly, you will need to provide your own icon components.

File type icons: CSV, DOCX, HTML, MD, ODT, PPT, PPTX, RAR, RTF, SEVEN_Z, TAR, TXT, XLS, XLSX

Reaction icons: LOLReaction, LoveReaction, ThumbsDownReaction, ThumbsUpReaction, WutReaction

Navigation / UI icons: ArrowLeft, ArrowRight, AtMentions, Attach, Back, ChatIcon, CheckSend, CircleClose, CirclePlus, CircleRight, Close, DownloadArrow, DownloadCloud, DragHandle, Error, Eye, Folder, GenericFile, GiphyLightning, Grid, Group, Logo, MailOpen, MenuPointVertical, MessageBubble, MessageIcon, Notification, PinHeader, Refresh, SendCheck, SendPoll, SendUp, ShareRightArrow, User, UserAdmin, UserMinus

Bespoke / Internal Notes

  • ReplyConnectorLeft, ReplyConnectorRight, and Unknown were intentionally left bespoke rather than being part of the export-backed asset migration.
  • EyeOpen is available in the V9 SDK icon set, but it remained a local/bespoke implementation rather than a renamed legacy export.
  • NewClose and the private chevron-up.tsx file are not barrel-exported from package/src/icons/index.ts, so treat them as internal implementation details rather than stable public icon exports.

MessageMenu (Context Menu)

The message context menu has been rebuilt around the current overlay system and its dedicated reaction and action components.

Although `MessageMenu` still exists in the public surface in `v9-latest`, the active runtime overlay path no longer renders it. If you customized `MessageMenu` in v8, migrate that work to `MessageReactionPicker`, `MessageActionList`, `MessageActionListItem`, and `MessageUserReactions` instead.

ActionType Union

The ActionType union has been expanded with blockUser:

type ActionType =
  | "banUser"
  | "blockUser" // new in V9
  | "copyMessage"
  | "deleteMessage"
  | "deleteForMeMessage"
  | "editMessage"
  | "flagMessage"
  | "markUnread"
  | "muteUser"
  | "pinMessage"
  | "selectReaction"
  | "reply"
  | "retry"
  | "quotedReply"
  | "threadReply"
  | "unpinMessage";

MessageActionType Structure

Actions now include a type field that separates standard actions from destructive ones — the menu renders them in distinct groups with a visual separator:

type MessageActionType = {
  action: () => void;
  actionType: ActionType | string;
  title: string;
  icon?: React.ReactElement;
  titleStyle?: StyleProp<TextStyle>;
  type: "standard" | "destructive";
};

Reaction Picker Changes

  • Emoji picker uses a 7-column grid layout.
  • New EmojiViewerButton component for expanding the full emoji list.
  • Reactions filter to isMain reactions by default.
  • Haptic feedback on reaction selection.
  • useHasOwnReaction hook for highlighting already-reacted emojis.

Theme Changes

The theme structure has undergone extensive changes across nearly every component. Below are the most impactful changes — review these carefully if you have custom theme overrides.

messageComposer Theme

Removed keys:

  • attachmentSeparator, autoCompleteInputContainer, composerContainer
  • commandInput (closeButton, container, text)
  • commandsButton, moreOptionsButton, optionsContainer
  • editingBoxContainer, editingBoxHeader, editingBoxHeaderTitle, editingStateHeader
  • replyContainer
  • searchIcon, sendRightIcon, sendUpIcon
  • audioRecordingButton (container, micIcon)
  • uploadProgressIndicator (container, indicatorColor, overlay)
  • cooldownTimer.container (only text remains)
  • imageUploadPreview.flatList

Added keys:

  • Layout: wrapper, contentContainer, inputContainer, inputBoxWrapper
  • Buttons: inputButtonsContainer, outputButtonsContainer, editButton, editButtonContainer, audioRecordingButtonContainer, cooldownButtonContainer
  • Floating: floatingWrapper, inputFloatingContainer
  • Upload indicators: fileUploadInProgressIndicator, fileUploadRetryIndicator, fileUploadNotSupportedIndicator, imageUploadInProgressIndicator, imageUploadRetryIndicator, imageUploadNotSupportedIndicator
  • Link previews: linkPreviewList (with linkContainer, linkIcon, container, imageWrapper, dismissWrapper, thumbnail, wrapper, metadataContainer, text, titleText)

Modified keys:

  • attachmentUploadPreviewList: from { filesFlatList, imagesFlatList, wrapper } to { flatList, itemSeparator }
  • imageAttachmentUploadPreview: itemContainercontainer, added wrapper
  • videoAttachmentUploadPreview: from { recorderIconContainer, recorderIcon, itemContainer, upload } to { durationContainer, durationText }

messageList Theme

Added keys:

  • inlineDateSeparatorContainer
  • unreadUnderlayContainer
  • scrollToBottomButtonContainer
  • stickyHeaderContainer
  • unreadMessagesNotificationContainer

Removed from scrollToBottomButton: touchable, wrapper, chevronColor

Modified unreadMessagesNotification:

  • Removed: closeButtonContainer, closeIcon, text
  • Added: leftButtonContainer, rightButtonContainer

messageItemView Theme

The old messageSimple section is renamed to messageItemView. This section has the most extensive changes.

content:

  • container simplified from ViewStyle & { borderRadiusL, borderRadiusS } to ViewStyle
  • Removed: deletedContainer, deletedContainerInner, deletedMetaText, deletedText, editedLabel, messageUser, metaContainer, receiverMessageBackgroundColor, senderMessageBackgroundColor
  • Added: contentContainer

New sub-components:

  • deletedcontainerInner, deletedText, container
  • footercontainer, name, editedText
  • bubbleWrapper, bubbleContentContainer, bubbleErrorContainer, bubbleReactionListTopContainer
  • savedForLaterHeadercontainer, label
  • reminderHeadercontainer, label, dot, time
  • sentToChannelHeadercontainer, label, dot, link
  • unsupportedAttachmentcontainer, details, title
  • contentContainer, leftAlignItems, rightAlignItems
  • messageGroupedSingleStyles, messageGroupedBottomStyles, messageGroupedTopStyles, messageGroupedMiddleStyles

authorWrapper (renamed from avatarWrapper): Removed leftAlign, rightAlign

cardUrlPreview:

  • Removed: authorName, authorNameContainer, authorNameFooter, authorNameFooterContainer, authorNameMask, noURI, playButtonStyle, playIcon
  • Added: linkPreview, linkPreviewText
  • New sub-component compactUrlPreview with wrapper, container, cardCover, cardFooter, title, description, linkPreview, linkPreviewText

giphy:

  • Removed: buttonContainer, cancel, giphyHeaderTitle, selectionContainer, send, shuffle, title
  • Added: actionButtonContainer, actionButton, actionButtonText, imageIndicatorContainer

reactionListBottom: Restructured from { contentContainer, item: { container, countText, filledBackgroundColor, icon, iconFillColor, iconSize, iconUnFillColor, unfilledBackgroundColor } } to { contentContainer, columnWrapper, rowSeparator }

New reaction list theme keys:

  • reactionListItemreactionCount, icon
  • reactionListClusteredcontentContainer, reactionCount, iconStyle, icon
  • reactionListItemWrapperwrapper, container

reactionListTop: Restructured from { container, item: { container, icon, iconFillColor, iconSize, iconUnFillColor, reactionSize }, position } to { container, contentContainer, list, position }. Default position changed from 16 to 8.

replies:

  • Removed: avatar, avatarContainerMultiple, avatarContainerSingle, leftAvatarsContainer, leftCurve, rightAvatarsContainer, rightCurve, avatarSize
  • Added: content, avatarStackContainer

status:

  • Removed: readByCount, statusContainer
  • Added: container

Gallery defaults changed:

PropertyV8V9
gridHeight195192
maxHeight300192
minHeight100120
minWidth170120

channelPreview Theme

Removed: avatar, checkAllIcon, checkIcon, row, mutedStatus (old structure with height, iconStyle, width)

Added:

  • messageDeliveryStatuscontainer, text, checkAllIcon, checkIcon, timeIcon
  • lowerRow, upperRow, statusContainer, titleContainer, wrapper
  • typingIndicatorPreviewcontainer, text
  • messagePreviewcontainer, subtitle
  • message expanded with subtitle, errorText, draftText

channelListSkeleton Theme

Completely restructured:

  • Removed: background, gradientStart, gradientStop, height, maskFillColor
  • Added: avatar, badge, content, headerRow, subtitle, textContainer, title
  • animationTime changed from 1800 to 1000

reply Theme

Completely redesigned:

  • Removed: fileAttachmentContainer, imageAttachment, markdownStyles, messageContainer, secondaryText, textContainer, videoThumbnail
  • Added: audioIcon, dismissWrapper, fileIcon, leftContainer, locationIcon, linkIcon, photoIcon, pollIcon, rightContainer, title, subtitle, subtitleContainer, videoIcon, wrapper, messagePreview

threadListItem Theme

Completely restructured:

  • Removed: boldText, contentRow, contentTextWrapper, headerRow, infoRow, parentMessagePreviewContainer, parentMessageText, touchableWrapper, unreadBubbleText
  • Added: wrapper, container, content, channelName, lowerRow, messageRepliesText, messagePreview, messagePreviewDeliveryStatus

Other Theme Changes

  • poll.answersList: buttonContainercontentContainer
  • poll.createContent: Added optionCardWrapper and expanded settings with description, optionCardContent, optionCardSwitch for addComment, anonymousPoll, multipleAnswers, suggestOption
  • poll.message.option: Removed color strings (progressBarEmptyFill, progressBarVotedFill, etc.), added info, header, votesText, progressBarContainer
  • progressControl: Removed filledColor
  • typingIndicator: Added loadingDotsBubble, avatarStackContainer
  • attachmentPicker: Removed errorButtonText, errorContainer, errorText; added content (container, infoContainer, text)
  • audioAttachment: Added centerContainer, audioInfo
  • threadListUnreadBanner: touchableWrapper renamed to container
  • thread.newThread: Restructured to include container
  • New top-level theme keys: channelDetailsMenu, threadListSkeleton

Migration Checklist

Follow these steps in order to migrate your project from V8 to V9:

1. Install New Dependencies

# Required
yarn add react-native-teleport

# Optional — for improved keyboard handling
yarn add react-native-keyboard-controller

Update stream-chat to ^9.36.0 and pin @gorhom/bottom-sheet to 5.1.8.

2. Update Avatar Usage

Replace Avatar with numeric size → new Avatar with string enum ('xs' through '2xl'). Rename image to imageUrl. Replace GroupAvatar with AvatarGroup or AvatarStack.

3. Update MessageComposer Customizations

Replace removed props with the new slot-based components:

If you used…Replace with…
CommandsButtonInternal CommandChip (automatic)
MoreOptionsButtonInputButtons
InputEditingStateHeaderMessageInputHeaderView
InputReplyStateHeaderMessageInputHeaderView
toggleAttachmentPickeropenAttachmentPicker / closeAttachmentPicker
selectedPickeruseAttachmentPickerState() hook

4. Update Attachment Picker Customizations

Replace individual selector icons (CameraSelectorIcon, FileSelectorIcon, etc.) with AttachmentTypePickerButton. Remove references to AttachmentPickerBottomSheetHandle, AttachmentPickerError, and AttachmentPickerErrorImage.

5. Update Upload Progress Indicators

Replace AttachmentUploadProgressIndicator with the six granular indicators (FileUploadInProgressIndicator, FileUploadRetryIndicator, FileUploadNotSupportedIndicator, ImageUploadInProgressIndicator, ImageUploadRetryIndicator, ImageUploadNotSupportedIndicator).

6. Update URL Preview Customizations

Replace Card / CardProps with UrlPreview / URLPreviewProps. Use the urlPreviewType prop on Channel to switch between 'full' and 'compact' modes.

7. Update Audio Hooks and Props

  • Rename useAudioControlleruseAudioRecorder.
  • Rename useAudioPlayerControluseAudioPlayer.
  • Rename asyncMessagesMultiSendEnabledaudioRecordingSendOnComplete in Channel, MessageComposer, and any useMessageInputContext() consumers.
  • Update the boolean semantics: audioRecordingSendOnComplete={true} now sends the recording immediately, while false keeps it in the composer.
  • Update the default behavior in your UX assumptions: recordings now send immediately by default.
  • Update any direct uploadVoiceRecording(...) calls from useMessageInputContext() to pass sendOnComplete semantics instead of multiSendEnabled.
  • Remove onLoad, onPlayPause, onProgress callbacks from AudioAttachment — use the useAudioPlayer hook instead.

Replace the imageGalleryCustomComponents nested object on OverlayProvider with direct component props (ImageGalleryHeader, ImageGalleryFooter, ImageGalleryVideoControls, ImageGalleryGrid).

9. Update ChannelPreview Customizations

  • Replace latestMessagePreview with lastMessage in any custom PreviewStatus or PreviewMessage components.
  • Rename ChannelPreviewMessengerChannelPreviewView in any direct imports, prop types, or ChannelList Preview={...} overrides.
  • Replace ChannelAvatar usage with the new ChannelAvatar from ui/Avatar/.
  • Rename useMutedUsersuseClientMutedUsers.
  • Keep the channelPreview theme namespace and channel-preview-button test ID unchanged.

10. Update Swipe-to-Reply and Message Content Customizations

  • Expect the full MessageItemView row, not only MessageBubble, to be swipeable when enableSwipeToReply is enabled.
  • Remove any custom MessageContent, MessageBubble, or ReactionListTop logic or tests that reference messageContentWidth or setMessageContentWidth.
  • Keep using messageSwipeToReplyHitSlop, customMessageSwipeAction, and MessageSwipeContent the same way; the gesture behavior itself is unchanged.
  • If you customize MessageItemView, attach contextMenuAnchorRef from useMessageContext() to the subview that should anchor the long-press overlay; top and bottom overlay items now align to that anchor instead of the full row.

11. Update MessageHeader Customizations

  • If you customized MessageHeader, stop relying on the old explicit prop bag from MessageSimple. In v9 beta, that row is now MessageItemView. Read data from useMessageContext() / useMessagesContext() instead, or override the dedicated header components (MessagePinnedHeader, MessageReminderHeader, MessageSavedForLaterHeader, SentToChannelHeader).

12. Update Theme Overrides

Review any custom theme overrides against the Theme Changes section. The most impacted areas are:

  • messageComposer — renamed from messageInput and completely restructured layout keys
  • messageItemView — new sub-components and flattened bubble keys (bubbleWrapper, bubbleContentContainer, bubbleErrorContainer, bubbleReactionListTopContainer)
  • channelPreview — new layout keys (upperRow, lowerRow, etc.)
  • reply — completely redesigned
  • threadListItem — completely restructured

13. Migrate Colors to Semantic Tokens

Replace raw colors.* usage with semantics.* tokens where available. This ensures your customizations adapt correctly across light, dark, and high-contrast themes.

If you already use semantic tokens directly, update the renamed tokens as well:

  • backgroundCoreSurface -> backgroundCoreSurfaceDefault
  • badgeTextInverse -> badgeTextOnInverse
  • textInverse -> textOnInverse
  • remove any dependency on backgroundCoreElevation4, which no longer exists in the semantic token contract

14. Remove Legacy Keyboard Workarounds

  • Set keyboardVerticalOffset to your header height on both Android and iOS.
  • Keep the same keyboardVerticalOffset everywhere the same chat layout is rendered.
  • Remove Android-specific negative offsets such as keyboardVerticalOffset={-300}.
  • Remove custom Android IME padding hacks that were only there to push MessageComposer above the keyboard.
  • Do not wrap MessageComposer in an extra SafeAreaView for bottom inset handling.
  • If you were relying on older V8 troubleshooting workarounds, re-test without them first. The refactored KeyboardCompatibleView should handle modern edge-to-edge layouts correctly.

15. Update Icon Imports

Audit icon imports carefully. Some public names were renamed (CircleStop -> Stop, Refresh -> Reload), some were removed entirely (Close, User, MessageIcon, ArrowRight, and many legacy file-type icons), and some kept the same export name but changed visually. If you were importing private icon files, also replace chevron-down / ChevronTop with chevron-up / ChevronUp. See Icons Changes for the full list.

16. Update FlatList Types

If you use additionalFlatListProps or setFlatListRef, update your types from LocalMessage to MessageListItemWithNeighbours.

17. Review messageContentOrder

The default order now places 'attachments' before 'text'. If your UI depends on the V8 order, pass it explicitly:

<Channel
  messageContentOrder={['quoted_reply', 'gallery', 'files', 'poll', 'ai_text', 'text', 'attachments', 'location']}
>

18. Update Message Action Types

If you customize message actions, note the new 'blockUser' action type and the type: 'standard' | 'destructive' classification that controls visual grouping in the menu.