# Required
yarn add react-native-teleport
# Optional — install for improved keyboard handling
yarn add react-native-keyboard-controllerUpgrading 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:
- Your project is running on React Native 0.76+ (or an Expo SDK version that defaults to the New Architecture).
- The New Architecture is enabled in your project configuration.
- 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:
| Package | Minimum Version | Required? | Purpose |
|---|---|---|---|
react-native-teleport | >=0.5.4 | Yes | Portal-based overlay system for the new message menus |
react-native-keyboard-controller | >=1.20.2 | No | Smoother, animation-driven keyboard interactions |
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 prebuildafter 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
| Package | V8 Version | V9 Version | Notes |
|---|---|---|---|
stream-chat | ^9.27.2 | ^9.36.0 | Includes new server-side features (reminders, save-for-later, etc.) |
@gorhom/bottom-sheet | ^5.1.8 | 5.1.8 | Pinned to an exact version to avoid regressions |
lodash-es | 4.17.21 | 4.17.23 | Patch-level bump |
linkifyjs | ^4.3.1 | ^4.3.2 | Patch-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:
| Layer | What it Contains |
|---|---|
primitives | Low-level building blocks — shadow, radius, spacing, and typography constants (e.g. primitives.spacingSm, primitives.radiusLg). |
foundations | 99 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. |
components | 38 component-specific dimension tokens for buttons, composers, devices, icons, and message bubbles. |
semantics | 199 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 equivalentsemantics.*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 Token | New Token |
|---|---|
backgroundCoreSurface | backgroundCoreSurfaceDefault |
badgeTextInverse | badgeTextOnInverse |
textInverse | textOnInverse |
backgroundCoreElevation4 | Removed with no direct replacement |
The token update also added new semantic tokens such as borderCoreOnInverse, borderCoreOnSurface, borderUtilityDisabledOnSurface, controlChipBorder, and controlChipText.
Default Value Changes
| Property | V8 Value | V9 Value | Notes |
|---|---|---|---|
screenPadding | 8 | 16 | Doubled for better visual spacing |
BASE_AVATAR_SIZE | 32 | 24 | Reduced to match new UI density |
loadingDots.spacing | 4 | primitives.spacingXxs | Now 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| Change | Details |
|---|---|
size | Now a string enum ('xs'–'2xl') instead of a pixel number |
image → imageUrl | Renamed for clarity |
name removed | Use placeholder for fallback content |
online / presenceIndicator | Removed — use the separate OnlineIndicator badge component instead |
ChannelAvatar | Moved to ui/Avatar/ChannelAvatar; now uses size='xl' by default |
GroupAvatar | Replaced 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
| Component | Purpose |
|---|---|
BadgeNotification | Unread counts and notification badges |
OnlineIndicator | User presence status (replaces Avatar's online prop) |
ErrorBadge | Error state indicators |
GiphyBadge | Giphy source badge on media |
ImgurBadge | Imgur source badge on media |
BadgeCount | Generic numeric badge |
Other New Primitives
| Component | Purpose |
|---|---|
Input | Standardized text input component |
VideoPlayIndicator | Play button overlay with size variants ('sm' / 'md' / 'lg') |
SpeedSettingsButton | Playback speed control for audio/video |
GiphyChip | Giphy 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 ModalNew 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:
| Prop | Description |
|---|---|
MessageComposerLeadingView | Left side of the composer — wraps InputButtons |
MessageComposerTrailingView | Right side of the composer — empty slot for custom trailing content |
MessageInputHeaderView | Header area — consolidates reply preview, edit header, attachments, and link previews |
MessageInputFooterView | Footer area below the input — empty slot for custom content |
MessageInputLeadingView | Leading area of the input text row |
MessageInputTrailingView | Trailing area — wraps OutputButtons (Send / Edit / Cooldown / Audio) |
InputView | The text input view itself |
messageInputFloating | boolean — enables a floating input layout (default: false) |
Removed Props
The following props are removed from Channel and MessageInputContext:
| Removed Prop | What to Use Instead |
|---|---|
CommandsButton | Commands are handled internally via the new CommandChip component |
MoreOptionsButton | Consolidated into InputButtons |
InputEditingStateHeader | Consolidated into MessageInputHeaderView |
InputReplyStateHeader | Consolidated into MessageInputHeaderView |
CommandInput | Replaced by the internal CommandChip component |
AttachmentPickerBottomSheetHandle | Removed — the attachment picker has been fully redesigned |
attachmentPickerBottomSheetHandleHeight | Removed |
CameraSelectorIcon | Replaced by AttachmentTypePickerButton |
FileSelectorIcon | Replaced by AttachmentTypePickerButton |
ImageSelectorIcon | Replaced by AttachmentTypePickerButton |
VideoRecorderSelectorIcon | Replaced by AttachmentTypePickerButton |
CreatePollIcon | Poll creation is handled internally |
AttachmentPickerError | Removed |
AttachmentPickerErrorImage | Removed |
AttachmentUploadProgressIndicator | Split into six granular indicators (see Upload Indicators) |
cooldownEndsAt | Cooldown is now managed internally by OutputButtons |
selectedPicker | Removed from context — use useAttachmentPickerState() hook |
toggleAttachmentPicker | Replaced by openAttachmentPicker / closeAttachmentPicker |
New Upload Indicator Components
The single AttachmentUploadProgressIndicator is replaced by six specific components, giving you finer control over each upload state:
| Component | Purpose |
|---|---|
FileUploadInProgressIndicator | Progress indicator for file uploads |
FileUploadRetryIndicator | Retry button for failed file uploads |
FileUploadNotSupportedIndicator | Indicator for unsupported file types |
ImageUploadInProgressIndicator | Progress indicator for image uploads |
ImageUploadRetryIndicator | Retry button for failed image uploads |
ImageUploadNotSupportedIndicator | Indicator for unsupported images |
OutputButtons Component
A new OutputButtons component manages the trailing action buttons using priority-based rendering with animated transitions (ZoomIn/ZoomOut, 200ms):
| Priority | Condition | Button Shown |
|---|---|---|
| 1 | AI streaming is active | StopMessageStreamingButton |
| 2 | User is editing a message or command | EditButton |
| 3 | Slow-mode cooldown is active | CooldownTimer |
| 4 | Audio recording enabled, no text/attachments | StartAudioRecordingButton |
| 5 | Default | SendButton |
Component Path Changes
| V8 Path | V9 Path |
|---|---|
MessageInput/AttachButton | MessageInput/components/InputButtons/AttachButton |
MessageInput/SendButton | MessageInput/components/OutputButtons/SendButton |
MessageInput/CooldownTimer | MessageInput/components/OutputButtons/CooldownTimer |
MessageInput/InputButtons | MessageInput/components/InputButtons |
MessageInput/AttachmentUploadPreviewList | MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList |
New MessageComposer Components
| Component | Description |
|---|---|
CommandChip | Displays the active slash command with a dismiss button |
EditButton | Confirm button shown when editing a message |
LinkPreviewList | Renders URL preview cards inline in the input area |
MicPositionContext | Context provider for audio recording microphone position |
MessageInputContext Changes
Removed Values
| Removed Value | What to Use Instead |
|---|---|
asyncMessagesMultiSendEnabled | audioRecordingSendOnComplete with inverted semantics |
cooldownEndsAt | Managed internally in OutputButtons |
selectedPicker | useAttachmentPickerState() hook |
toggleAttachmentPicker | openAttachmentPicker / closeAttachmentPicker |
getMessagesGroupStyles | Removed — grouping logic is now internal |
legacyImageViewerSwipeBehaviour | Removed — the legacy image viewer is no longer supported |
Added Values
| New Value | Type | Description |
|---|---|---|
audioRecordingSendOnComplete | boolean | Whether a completed voice recording is sent immediately |
audioRecorderManager | AudioRecorderManager | Manages 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 |
messageInputFloating | boolean | Whether the input is using floating layout |
messageInputHeightStore | MessageInputHeightStore | State store for tracking input height |
createPollOptionGap | number | Gap between poll options in creation UI |
Modified Values
| Value | V8 Type | V9 Type |
|---|---|---|
takeAndUploadImage | () => Promise<void> | () => Promise<{askToOpenSettings?; canceled?}> |
CooldownTimer | ComponentType<CooldownTimerProps> | ComponentType (no props — self-contained) |
AudioRecordingPreview | ComponentType<AudioRecordingPreviewProps> | ComponentType (no props — self-contained) |
VideoAttachmentUploadPreview | ComponentType<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:
asyncMessagesMultiSendEnableddefaulted totrue, so recordings did not send immediately. - Now:
audioRecordingSendOnCompletedefaults totrue, 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 Prop | Notes |
|---|---|
onLoad | Replaced by the useAudioPlayer hook |
onPlayPause | Replaced by the useAudioPlayer hook |
onProgress | Replaced by the useAudioPlayer hook |
titleMaxLength | Removed — use showTitle boolean instead |
Added Props
| New Prop | Type | Description |
|---|---|---|
showTitle | boolean | Controls whether the audio title is shown |
containerStyle | ViewStyle | Style override for the container |
indicator | ReactElement | Custom indicator element |
styles | object | Granular style overrides |
Hook Rename
| V8 Hook | V9 Hook |
|---|---|
useAudioController | useAudioRecorder |
useAudioPlayerControl | useAudioPlayer |
MessageList Changes
New Props
| Prop | Type | Description |
|---|---|---|
animateLayout | boolean | Enables layout animations for message insertion/removal (default: true) |
messageInputFloating | boolean | Tells the list whether the input is in floating mode |
messageInputHeightStore | state store | State store for coordinating with the input's height |
InlineDateSeparator | Component | Customizable inline date separator rendered between messages |
InlineUnreadIndicator | Component | Customizable inline unread indicator rendered at the unread boundary |
Message | Component | Override the default message component |
attachmentPickerStore | state store | State store for the attachment picker |
Removed Props
| Prop | Notes |
|---|---|
isListActive | Removed |
legacyImageViewerSwipeBehaviour | Removed — the legacy image viewer is no longer supported |
setMessages | Removed — use the centralized state store instead |
selectedPicker | Replaced by attachmentPickerStore |
setSelectedPicker | Replaced by attachmentPickerStore |
Modified Props
| Prop | V8 Type | V9 Type |
|---|---|---|
additionalFlatListProps | Partial<FlatListProps<LocalMessage>> | Partial<FlatListProps<MessageListItemWithNeighbours>> |
setFlatListRef | FlatListType<LocalMessage> | FlatListType<MessageListItemWithNeighbours> |
channelUnreadState | Direct state value | Replaced 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
| Component | Description |
|---|---|
ScrollToBottomButton | Redesigned with animated entrance — uses the new Button and BadgeNotification components |
StickyHeader | Sticky date header that tracks the scroll position |
UnreadMessagesNotification | Redesigned with a two-button layout: "Jump to unread" and dismiss |
MessagesContext Changes
Removed Values
| Removed Value | What to Use Instead |
|---|---|
AttachmentActions | Removed — actions are now inline on attachments |
Card | Replaced by UrlPreview with new URLPreviewProps |
CardCover / CardFooter / CardHeader | Removed — the card component is redesigned as UrlPreview |
ImageReloadIndicator | Removed |
MessageEditedTimestamp | Folded into the message footer |
getMessagesGroupStyles | Removed — grouping is handled internally |
legacyImageViewerSwipeBehaviour | Removed |
Added Values
| New Value | Type | Description |
|---|---|---|
urlPreviewType | 'compact' | 'full' | Controls URL preview style (compact card vs. full embed) |
URLPreviewCompact | ComponentType<URLPreviewCompactProps> | Compact URL preview component |
FilePreview | ComponentType<FilePreviewProps> | Standalone file preview component |
MessageReminderHeader | ComponentType<MessageReminderHeaderProps> | Header shown on reminded messages |
MessageSavedForLaterHeader | ComponentType<MessageSavedForLaterHeaderProps> | Header shown on saved-for-later messages |
SentToChannelHeader | ComponentType<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 |
ReactionListClustered | Component | Clustered reaction list display |
ReactionListItem | Component | Individual reaction item |
ReactionListItemWrapper | Component | Wrapper for reaction items |
ReactionListCountItem | Component | Reaction count display |
UnsupportedAttachment | ComponentType<UnsupportedAttachmentProps> | Fallback renderer for unsupported/custom attachment types |
Modified Values
| Value | V8 Type | V9 Type |
|---|---|---|
UrlPreview | ComponentType<CardProps> | ComponentType<URLPreviewProps> |
MessageItemView | ComponentType<MessageSimpleProps> | ComponentType<MessageItemViewProps> |
MessageHeader | Optional ComponentType<MessageFooterProps> | Required ComponentType<MessageHeaderProps> |
Note:
MessageHeaderis now a required value in the context. If you were relying on it being optional, make sure your custom provider supplies a component.
MessageSimple → MessageItemView
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.
MessageSimple→MessageItemViewMessageSimpleProps→MessageItemViewPropsMessageSimplePropsWithContext→MessageItemViewPropsWithContext<Channel MessageSimple={...} />→<Channel MessageItemView={...} />messagesContext.MessageSimple→messagesContext.MessageItemViewmessageSimpletheme key →messageItemViewmessage-simple-wrappertest ID →message-item-view-wrapperMessage/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.
MessageAvatar → MessageAuthor
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.
MessageAvatar→MessageAuthorMessageAvatarProps→MessageAuthorPropsMessageAvatarPropsWithContext→MessageAuthorPropsWithContext<Channel MessageAvatar={...} />→<Channel MessageAuthor={...} />messagesContext.MessageAvatar→messagesContext.MessageAuthormessageItemView.avatarWrapper→messageItemView.authorWrappermessage-avatartest ID →message-authorMessage/MessageSimple/MessageAvatar→Message/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: {},
},
},
};MessageInput → MessageComposer
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.
MessageInput→MessageComposerMessageInputProps→MessageComposerProps<MessageInput />→<MessageComposer />import { MessageInput } from 'stream-chat-react-native'→import { MessageComposer } from 'stream-chat-react-native'theme.messageInput→theme.messageComposerThread.MessageInput→Thread.MessageComposeradditionalMessageInputProps→additionalMessageComposerPropsMessageInput/MessageInput→MessageComposer/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.
ChannelListMessenger → ChannelListView
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.
ChannelListMessenger→ChannelListViewChannelListMessengerProps→ChannelListViewPropsChannelListMessengerPropsWithContext→ChannelListViewPropsWithContextimport { ChannelListMessenger } from 'stream-chat-react-native'→import { ChannelListView } from 'stream-chat-react-native'theme.channelListMessenger→theme.channelListViewchannel-list-messengertest ID →channel-list-viewChannelList/ChannelListMessenger→ChannelList/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} />;ChannelPreviewMessenger → ChannelPreviewView
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.
ChannelPreviewMessenger→ChannelPreviewViewChannelPreviewMessengerProps→ChannelPreviewViewPropsChannelPreviewMessengerPropsWithContext→ChannelPreviewViewPropsWithContextimport { ChannelPreviewMessenger } from 'stream-chat-react-native'→import { ChannelPreviewView } from 'stream-chat-react-native'<ChannelList Preview={ChannelPreviewMessenger} />→<ChannelList Preview={ChannelPreviewView} />ChannelPreview/ChannelPreviewMessenger→ChannelPreview/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 Prop | V9 Source |
|---|---|
alignment | useMessageContext() |
message | useMessageContext() or optional message prop |
date | message.created_at |
isDeleted | message.type === 'deleted' |
lastGroupMessage | useMessageContext() |
members | useMessageContext() |
MessageStatus | useMessagesContext() |
otherAttachments | useMessageContext() |
showMessageStatus | useMessageContext() |
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
messageSwipeToReplyHitSlophandling still applies. - Horizontal activation, thresholds, haptics,
MessageSwipeContent, and spring-back behavior are unchanged. - Deleted messages are still not swipeable.
- When
enableSwipeToReplyisfalse, 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.
MessageBubbleis no longer the swipe boundary;MessageItemViewis.messageContentWidthwas removed fromReactionListTop.setMessageContentWidthwas removed fromMessageContent.- The underlying
MessageBubble/MessageItemViewchain 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
| Prop | Type | Default | Description |
|---|---|---|---|
messageInputFloating | boolean | false | Enables the floating message input layout |
handleBlockUser | function | — | Callback for blocking a user |
reactionListType | 'clustered' | 'default' | 'clustered' | Switches between clustered and default reaction lists |
urlPreviewType | 'full' | 'compact' | 'full' | Controls URL preview display style |
onAlsoSentToChannelHeaderPress | function | — | Press handler for thread "also sent to channel" header |
asyncMessagesLockDistance | number | 50 | Audio recording gesture: lock distance threshold |
asyncMessagesMinimumPressDuration | number | 500 | Audio recording gesture: minimum press duration (ms) |
audioRecordingSendOnComplete | boolean | true | Whether a completed voice recording is sent immediately |
asyncMessagesSlideToCancelDistance | number | 75 | Audio recording gesture: slide-to-cancel distance |
createPollOptionGap | number | — | Gap between poll options in the creation UI |
MessageReminderHeader | Component | — | Custom header for message reminders |
MessageSavedForLaterHeader | Component | — | Custom header for saved messages |
SentToChannelHeader | Component | — | Custom header for "also sent to channel" thread messages |
ReactionListClustered | Component | — | Custom clustered reaction list |
ReactionListCountItem | Component | — | Custom reaction count item |
ReactionListItem | Component | — | Custom reaction list item |
ReactionListItemWrapper | Component | — | Custom reaction list item wrapper |
URLPreviewCompact | Component | — | Custom compact URL preview |
FilePreview | Component | — | Custom file preview component |
UnsupportedAttachment | Component | — | Fallback 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 Prop | Notes |
|---|---|
legacyImageViewerSwipeBehaviour | Legacy image viewer is removed |
isAttachmentEqual | Removed |
MessageEditedTimestamp | Folded into the message footer |
ImageReloadIndicator | Removed |
AttachmentActions | Removed — actions are inline on attachments |
Card / CardCover / CardFooter / CardHeader | Replaced by UrlPreview / URLPreviewCompact |
AttachmentPickerIOSSelectMorePhotos | Removed |
| All attachment picker selector icons | See MessageComposer — Removed Props |
Modified Default Values
| Prop | V8 Default | V9 Default | Impact |
|---|---|---|---|
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 |
numberOfAttachmentImagesToLoadPerCall | 60 | 25 | Reduced for better performance on lower-end devices |
attachmentPickerBottomSheetHeight | vh(45) (viewport-based) | disableAttachmentPicker ? 72 : 333 (fixed) | Switched from viewport-relative to fixed pixel values |
attachmentSelectionBarHeight | 52 | 72 | Increased for better touch targets |
Action required: If your UI depends on
'text'rendering before'attachments', explicitly pass the V8 order array tomessageContentOrder.
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: latestMessagePreview → lastMessage
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
| Component | Description |
|---|---|
ChannelDetailsBottomSheet | Bottom sheet overlay for channel details and actions |
ChannelLastMessagePreview | Formatted last message preview with attachment/poll awareness |
ChannelMessagePreviewDeliveryStatus | Delivery status indicator in the channel preview |
ChannelPreviewMutedStatus | Muted state indicator with configurable position |
ChannelPreviewTypingIndicator | Typing indicator shown in the channel preview |
Removed Components
| Removed Component | Notes |
|---|---|
ChannelAvatar | Replaced by ChannelAvatar from ui/Avatar/ (different API) |
PreviewAvatar | Removed from context — use ChannelAvatar with size='xl' |
New Props
| Prop | Type | Description |
|---|---|---|
mutedStatusPosition | 'inlineTitle' | 'trailingBottom' | Where the muted icon appears in the channel preview |
New Hooks
| Hook | Description |
|---|---|
useChannelPreviewDraftMessage | Access draft message data for a channel |
useChannelPreviewPollLabel | Get a formatted label for the last poll |
useChannelTypingState | Observe 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 Hook | V9 Hook |
|---|---|
useMutedUsers (from Chat) | useClientMutedUsers |
Attachment Components
Renamed Components
| V8 Component | V9 Component | Notes |
|---|---|---|
AudioAttachment | Audio | Renamed; file moved to Audio/ subdirectory |
Card | UrlPreview | Renamed with new URLPreviewProps interface |
Removed Components
| Removed Component | Notes |
|---|---|
AttachmentActions | Removed — actions are now handled inline on attachments |
ImageReloadIndicator | Removed |
AttachmentUnsupportedIndicator | Removed |
New Components
| Component | Description |
|---|---|
URLPreviewCompact | Compact URL preview with card-style layout |
UnsupportedAttachment | Replaces the old unsupported indicator with a full component |
AttachmentPicker Overhaul
The attachment picker has been completely redesigned with a simpler, more extensible architecture.
Removed:
AttachmentPickerBottomSheetHandleAttachmentPickerError/AttachmentPickerErrorImageCameraSelectorIcon/FileSelectorIcon/ImageSelectorIcon/VideoRecorderSelectorIconAttachmentPickerIOSSelectMorePhotosAttachmentPickerItem
Added:
| Component | Description |
|---|---|
AttachmentPickerContent | Main content area of the redesigned picker |
AttachmentMediaPicker | Media selection UI (photos and videos) |
AttachmentTypePickerButton | Unified 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
| Prop | Type | Description |
|---|---|---|
MessageOverlayBackground | React.ComponentType | Custom background component for the message overlay |
Removed Props
| Removed Prop | What to Use Instead |
|---|---|
imageGalleryGridHandleHeight | Removed |
imageGalleryGridSnapPoints | Removed |
imageGalleryCustomComponents | Replaced by direct component props (see below) |
Image Gallery Customization — Flattened
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:
ImageGalleryAssettype replaces the oldPhototype.- Gallery configuration (
autoPlayVideo,giphyVersion) moves from props toImageGalleryOptionsin the state store. - Three utility functions are exported:
isViewableImageAttachment,isViewableVideoAttachment,isViewableGiphyAttachment.
// V8
<Gallery autoPlayVideo={true} giphyVersion="original" />;
// V9
const { imageGalleryStateStore } = useImageGalleryContext();
imageGalleryStateStore.openImageGallery(/* ... */);Removed
| Removed | Notes |
|---|---|
imageGalleryCustomComponents | Replaced by direct component props |
autoPlayVideo (as prop) | Moved to ImageGalleryOptions in state store |
giphyVersion (as prop) | Moved to ImageGalleryOptions in state store |
imageGalleryGridSnapPoints | Removed |
imageGalleryGridHandleHeight | Removed |
ImageGalleryOverlay | Removed |
ImageGridHandle | Removed |
legacyImageViewerSwipeBehaviour | Removed |
setMessages / setSelectedMessage | Use imageGalleryStateStore instead |
Added
| Added | Description |
|---|---|
ImageGalleryVideoControls | Component prop for video playback controls |
ImageGalleryVideoControl | Video controls UI component |
useImageGalleryVideoPlayer | Hook for managing video player state |
Message Components
New Components
| Component | Description |
|---|---|
MessageHeader | Consolidated message header — now required in MessagesContext |
MessageBlocked | UI for blocked messages |
MessageSwipeContent | Content revealed on message swipe |
MessageTextContainer | Wrapper for message text with consistent styling |
ReactionListClustered | Clustered reaction list display (new default style) |
ReactionListItem | Individual reaction item component |
ReactionListItemWrapper | Wrapper for reaction items |
Removed Components
| Removed Component | Notes |
|---|---|
MessageEditedTimestamp | Folded into the message footer component |
MessagePreview | Replaced 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:
| Hook | Description |
|---|---|
useMessageDeliveryStatus | Returns delivery status data for a message preview |
useGroupedAttachments | Returns grouped attachment data for a message |
useMessagePreviewIcon | Determines the appropriate icon for a preview |
useMessagePreviewText | Formats 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:
VideoPlayerclass withVideoPlayerState,VideoDescriptor,VideoPlayerOptionstypesDEFAULT_PLAYBACK_RATESandINITIAL_VIDEO_PLAYER_STATEconstants
VideoPlayerPool
Pool management for multiple concurrent video players:
VideoPlayerPoolclass withVideoPlayerPoolStatetype
ImageGalleryStateStore
Centralized state for the image gallery (replaces in-component state):
ImageGalleryStateStoreclassImageGalleryAsset,ImageGalleryState,ImageGalleryOptionstypesisViewableImageAttachment,isViewableVideoAttachment,isViewableGiphyAttachmentutilities
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
| Hook | Description |
|---|---|
useAttachmentPickerState | Manages attachment picker open/closed state (replaces context-based approach) |
useAudioRecorder | Audio recording lifecycle (replaces useAudioController) |
useAudioPlayer | Audio playback control (replaces useAudioPlayerControl) |
useMessageDeliveryStatus | Message delivery status for previews |
useGroupedAttachments | Grouped attachment data for message previews |
useMessagePreviewIcon | Icon selection logic for message previews |
useMessagePreviewText | Text formatting for message previews |
useImageGalleryVideoPlayer | Video player state within the image gallery |
useMessageComposer | Direct access to message composer state and methods |
Renamed Hooks
| V8 Hook | V9 Hook |
|---|---|
useAudioController | useAudioRecorder |
useAudioPlayerControl | useAudioPlayer |
useMutedUsers | useClientMutedUsers |
New Contexts
| Context | Description |
|---|---|
BottomSheetContext | Provides 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 Import | Replacement | Notes |
|---|---|---|
chevron-down | chevron-up | The private chevron-down.tsx file was removed |
ChevronTop | ChevronUp | Rename 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 Icon | V9 Icon / Guidance | Notes |
|---|---|---|
CircleStop | Stop | Consolidated into the Stop export |
Refresh | Reload | Use Reload instead of Refresh |
ArrowRight | No SDK replacement | Provide your own local icon if you still need this glyph |
Close | No SDK replacement | Provide your own local icon if you still need this glyph |
MessageIcon | No SDK replacement | Provide your own local icon if you still need this glyph |
User | No SDK replacement | Provide your own local icon if you still need this glyph |
| Legacy file-type exports | New file-type icon family | Legacy file-type exports were consolidated into new V9 icons |
New Icons
| Icon | Description |
|---|---|
BlockUser | Block user action |
Plus | Add/create action |
Minus | Remove action |
ArrowShareLeft | Share/forward action |
Bell | Notification/reminder bell |
Bookmark | Save for later / bookmark |
Checkmark | Confirmation checkmark |
CommandsIcon | Slash commands |
FilePickerIcon | File 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:
NewClosenow renders the tighterxmark-smallglyph, even though the component name is unchanged.CommandsIcon,File,Link,Pause,Plus,Stop, andThreadReplywere 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, andUnknownwere intentionally left bespoke rather than being part of the export-backed asset migration.EyeOpenis available in the V9 SDK icon set, but it remained a local/bespoke implementation rather than a renamed legacy export.NewCloseand the privatechevron-up.tsxfile are not barrel-exported frompackage/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.
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
EmojiViewerButtoncomponent for expanding the full emoji list. - Reactions filter to
isMainreactions by default. - Haptic feedback on reaction selection.
useHasOwnReactionhook 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,composerContainercommandInput(closeButton,container,text)commandsButton,moreOptionsButton,optionsContainereditingBoxContainer,editingBoxHeader,editingBoxHeaderTitle,editingStateHeaderreplyContainersearchIcon,sendRightIcon,sendUpIconaudioRecordingButton(container,micIcon)uploadProgressIndicator(container,indicatorColor,overlay)cooldownTimer.container(onlytextremains)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(withlinkContainer,linkIcon,container,imageWrapper,dismissWrapper,thumbnail,wrapper,metadataContainer,text,titleText)
Modified keys:
attachmentUploadPreviewList: from{ filesFlatList, imagesFlatList, wrapper }to{ flatList, itemSeparator }imageAttachmentUploadPreview:itemContainer→container, addedwrappervideoAttachmentUploadPreview: from{ recorderIconContainer, recorderIcon, itemContainer, upload }to{ durationContainer, durationText }
messageList Theme
Added keys:
inlineDateSeparatorContainerunreadUnderlayContainerscrollToBottomButtonContainerstickyHeaderContainerunreadMessagesNotificationContainer
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:
containersimplified fromViewStyle & { borderRadiusL, borderRadiusS }toViewStyle- Removed:
deletedContainer,deletedContainerInner,deletedMetaText,deletedText,editedLabel,messageUser,metaContainer,receiverMessageBackgroundColor,senderMessageBackgroundColor - Added:
contentContainer
New sub-components:
deleted—containerInner,deletedText,containerfooter—container,name,editedTextbubbleWrapper,bubbleContentContainer,bubbleErrorContainer,bubbleReactionListTopContainersavedForLaterHeader—container,labelreminderHeader—container,label,dot,timesentToChannelHeader—container,label,dot,linkunsupportedAttachment—container,details,titlecontentContainer,leftAlignItems,rightAlignItemsmessageGroupedSingleStyles,messageGroupedBottomStyles,messageGroupedTopStyles,messageGroupedMiddleStyles
authorWrapper (renamed from avatarWrapper): Removed leftAlign, rightAlign
card → UrlPreview:
- Removed:
authorName,authorNameContainer,authorNameFooter,authorNameFooterContainer,authorNameMask,noURI,playButtonStyle,playIcon - Added:
linkPreview,linkPreviewText - New sub-component
compactUrlPreviewwithwrapper,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:
reactionListItem—reactionCount,iconreactionListClustered—contentContainer,reactionCount,iconStyle,iconreactionListItemWrapper—wrapper,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:
| Property | V8 | V9 |
|---|---|---|
gridHeight | 195 | 192 |
maxHeight | 300 | 192 |
minHeight | 100 | 120 |
minWidth | 170 | 120 |
channelPreview Theme
Removed: avatar, checkAllIcon, checkIcon, row, mutedStatus (old structure with height, iconStyle, width)
Added:
messageDeliveryStatus—container,text,checkAllIcon,checkIcon,timeIconlowerRow,upperRow,statusContainer,titleContainer,wrappertypingIndicatorPreview—container,textmessagePreview—container,subtitlemessageexpanded withsubtitle,errorText,draftText
channelListSkeleton Theme
Completely restructured:
- Removed:
background,gradientStart,gradientStop,height,maskFillColor - Added:
avatar,badge,content,headerRow,subtitle,textContainer,title animationTimechanged from1800to1000
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:buttonContainer→contentContainerpoll.createContent: AddedoptionCardWrapperand expanded settings withdescription,optionCardContent,optionCardSwitchforaddComment,anonymousPoll,multipleAnswers,suggestOptionpoll.message.option: Removed color strings (progressBarEmptyFill,progressBarVotedFill, etc.), addedinfo,header,votesText,progressBarContainerprogressControl: RemovedfilledColortypingIndicator: AddedloadingDotsBubble,avatarStackContainerattachmentPicker: RemovederrorButtonText,errorContainer,errorText; addedcontent(container,infoContainer,text)audioAttachment: AddedcenterContainer,audioInfothreadListUnreadBanner:touchableWrapperrenamed tocontainerthread.newThread: Restructured to includecontainer- 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-controllerUpdate 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… |
|---|---|
CommandsButton | Internal CommandChip (automatic) |
MoreOptionsButton | InputButtons |
InputEditingStateHeader | MessageInputHeaderView |
InputReplyStateHeader | MessageInputHeaderView |
toggleAttachmentPicker | openAttachmentPicker / closeAttachmentPicker |
selectedPicker | useAttachmentPickerState() 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
useAudioController→useAudioRecorder. - Rename
useAudioPlayerControl→useAudioPlayer. - Rename
asyncMessagesMultiSendEnabled→audioRecordingSendOnCompleteinChannel,MessageComposer, and anyuseMessageInputContext()consumers. - Update the boolean semantics:
audioRecordingSendOnComplete={true}now sends the recording immediately, whilefalsekeeps it in the composer. - Update the default behavior in your UX assumptions: recordings now send immediately by default.
- Update any direct
uploadVoiceRecording(...)calls fromuseMessageInputContext()to passsendOnCompletesemantics instead ofmultiSendEnabled. - Remove
onLoad,onPlayPause,onProgresscallbacks fromAudioAttachment— use theuseAudioPlayerhook instead.
8. Update Image Gallery Customizations
Replace the imageGalleryCustomComponents nested object on OverlayProvider with direct component props (ImageGalleryHeader, ImageGalleryFooter, ImageGalleryVideoControls, ImageGalleryGrid).
9. Update ChannelPreview Customizations
- Replace
latestMessagePreviewwithlastMessagein any customPreviewStatusorPreviewMessagecomponents. - Rename
ChannelPreviewMessenger→ChannelPreviewViewin any direct imports, prop types, orChannelList Preview={...}overrides. - Replace
ChannelAvatarusage with the newChannelAvatarfromui/Avatar/. - Rename
useMutedUsers→useClientMutedUsers. - Keep the
channelPreviewtheme namespace andchannel-preview-buttontest ID unchanged.
10. Update Swipe-to-Reply and Message Content Customizations
- Expect the full
MessageItemViewrow, not onlyMessageBubble, to be swipeable whenenableSwipeToReplyis enabled. - Remove any custom
MessageContent,MessageBubble, orReactionListToplogic or tests that referencemessageContentWidthorsetMessageContentWidth. - Keep using
messageSwipeToReplyHitSlop,customMessageSwipeAction, andMessageSwipeContentthe same way; the gesture behavior itself is unchanged. - If you customize
MessageItemView, attachcontextMenuAnchorReffromuseMessageContext()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 fromMessageSimple. In v9 beta, that row is nowMessageItemView. Read data fromuseMessageContext()/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 frommessageInputand completely restructured layout keysmessageItemView— new sub-components and flattened bubble keys (bubbleWrapper,bubbleContentContainer,bubbleErrorContainer,bubbleReactionListTopContainer)channelPreview— new layout keys (upperRow,lowerRow, etc.)reply— completely redesignedthreadListItem— 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->backgroundCoreSurfaceDefaultbadgeTextInverse->badgeTextOnInversetextInverse->textOnInverse- remove any dependency on
backgroundCoreElevation4, which no longer exists in the semantic token contract
14. Remove Legacy Keyboard Workarounds
- Set
keyboardVerticalOffsetto your header height on both Android and iOS. - Keep the same
keyboardVerticalOffseteverywhere 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
MessageComposerabove the keyboard. - Do not wrap
MessageComposerin an extraSafeAreaViewfor bottom inset handling. - If you were relying on older V8 troubleshooting workarounds, re-test without them first. The refactored
KeyboardCompatibleViewshould 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.
- React Native New Architecture Required
- New Peer Dependencies
- Keyboard Handling Changes
- Dependency Version Bumps
- Design Token System
- New Base UI Component Library
- MessageComposer — Complete Redesign
- MessageInputContext Changes
- AudioAttachment Changes
- MessageList Changes
- MessagesContext Changes
- Channel Component Changes
- ChannelPreview Changes
- Attachment Components
- OverlayProvider Changes
- ImageGallery Changes
- Message Components
- State Store Additions
- New Hooks
- New Contexts
- Icons Changes
- MessageMenu (Context Menu)
- Theme Changes
- Migration Checklist
- 1. Install New Dependencies
- 2. Update Avatar Usage
- 3. Update MessageComposer Customizations
- 4. Update Attachment Picker Customizations
- 5. Update Upload Progress Indicators
- 6. Update URL Preview Customizations
- 7. Update Audio Hooks and Props
- 8. Update Image Gallery Customizations
- 9. Update ChannelPreview Customizations
- 10. Update Swipe-to-Reply and Message Content Customizations
- 11. Update MessageHeader Customizations
- 12. Update Theme Overrides
- 13. Migrate Colors to Semantic Tokens
- 14. Remove Legacy Keyboard Workarounds
- 15. Update Icon Imports
- 16. Update FlatList Types
- 17. Review messageContentOrder
- 18. Update Message Action Types