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.


AI-Assisted Migration

If you have Stream's agent skills installed, the quickest path is the built-in /stream-react-native skill. Point it at your project and let it run the upgrade for you:

/stream-react-native Upgrade stream-chat-react-native to v9 in this project.

Prefer to drive the migration yourself, or use a different AI assistant? Use a prompt like this:

Migrate this app from stream-chat-react-native v8 to v9.

Use the installed SDK in node_modules as the source of truth.
Before using any symbol, prop, or export path, verify it in node_modules/stream-chat-react-native-core/src/index.ts.
The SDK source lives in node_modules/stream-chat-react-native-core/src, not in the wrapper packages.
Also use this migration checklist as reference:
- https://github.com/GetStream/stream-chat-react-native/blob/develop/ai-docs/ai-migration.md

Do the migration in phases:
1. prerequisites and setup (React Native New Architecture, react-native-teleport)
2. move component overrides to WithComponents
3. import and symbol renames
4. removed API rewrites and MessageComposer migration
5. theme, keyboard, overlay, and runtime behavior audit

Stop after each phase and report the exact changes and remaining errors.

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 Dependency

V9 introduces one new required peer dependency:

Package Minimum Version Required? Purpose
react-native-teleport >=0.5.4 Yes Portal-based overlay system for the new message menus
yarn add react-native-teleport

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 and topInset

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

In V9, pass the same value as topInset on Channel whenever a native navigation header is present. The attachment picker bottom sheet uses topInset to compute its top boundary; without it, the sheet is clamped short of its full snap point. In V8 and earlier, the optional react-native-keyboard-controller integration handled this implicitly via edge-to-edge mode — V9 no longer depends on that library, so the prop must be set explicitly.

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.


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 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 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 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 customize regions of the message input. Provide them via <WithComponents overrides={{ ... }}> (see Component Overrides: Props to WithComponents):

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 composer indicators, plus shared upload-progress primitives used by composer previews and message-list attachments:

Component Purpose
AttachmentUploadIndicator Shared pending upload indicator
CircularProgressIndicator Circular upload progress ring
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
MediaUploadProgressOverlay Full-cover media upload overlay

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: 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 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

Component Values Moved Out of MessagesContext

All ComponentType values previously exposed on MessagesContext (e.g., UrlPreview, MessageItemView, MessageHeader, Attachment, Reply, Gallery, etc.) have been moved to ComponentsContext and are now overridable exclusively via <WithComponents overrides={{ ... }}>. MessagesContext in V9 carries only data and callbacks.

If you wrapped your own MessagesContext provider directly, stop passing these keys into its value. Move them to a <WithComponents> wrapper — see Component Overrides: Props to WithComponents. The prop-type renames still apply where relevant (e.g., CardProps → URLPreviewProps, MessageSimpleProps → MessageItemViewProps).

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 → MessageItemView
  • MessageSimpleProps → MessageItemViewProps
  • MessageSimplePropsWithContext → MessageItemViewPropsWithContext
  • <Channel MessageSimple={...} /> → <WithComponents overrides={{ MessageItemView: ... }}>
  • messagesContext.MessageSimple → useComponentsContext().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:

<WithComponents overrides={{ MessageItemView: CustomMessageItemView }}>
  <Channel channel={channel}>{/* ... */}</Channel>
</WithComponents>;

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 → MessageAuthor
  • MessageAvatarProps → MessageAuthorProps
  • MessageAvatarPropsWithContext → MessageAuthorPropsWithContext
  • <Channel MessageAvatar={...} /> → <WithComponents overrides={{ MessageAuthor: ... }}>
  • messagesContext.MessageAvatar → useComponentsContext().MessageAuthor
  • messageItemView.avatarWrapper → messageItemView.authorWrapper
  • message-avatar test ID → message-author
  • Message/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";

<WithComponents overrides={{ MessageAuthor: CustomMessageAuthor }}>
  <Channel channel={channel}>{/* ... */}</Channel>
</WithComponents>;

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 → MessageComposer
  • MessageInputProps → MessageComposerProps
  • <MessageInput /> → <MessageComposer />
  • import { MessageInput } from 'stream-chat-react-native' → import { MessageComposer } from 'stream-chat-react-native'
  • theme.messageInput → theme.messageComposer
  • Thread.MessageInput → Thread.MessageComposer
  • additionalMessageInputProps → additionalMessageComposerProps
  • MessageInput/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>;

<WithComponents overrides={{ ThreadMessageComposer: CustomMessageComposer }}>
  <Thread additionalMessageComposerProps={{ autoFocus: true }} />
</WithComponents>;

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 → ChannelListView
  • ChannelListMessengerProps → ChannelListViewProps
  • ChannelListMessengerPropsWithContext → ChannelListViewPropsWithContext
  • import { ChannelListMessenger } from 'stream-chat-react-native' → import { ChannelListView } from 'stream-chat-react-native'
  • theme.channelListMessenger → theme.channelListView
  • channel-list-messenger test ID → channel-list-view
  • ChannelList/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: {},
  },
};

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 → ChannelPreviewView
  • ChannelPreviewMessengerProps → ChannelPreviewViewProps
  • ChannelPreviewMessengerPropsWithContext → ChannelPreviewViewPropsWithContext
  • import { ChannelPreviewMessenger } from 'stream-chat-react-native' → import { ChannelPreviewView } from 'stream-chat-react-native'
  • <ChannelList Preview={ChannelPreviewMessenger} /> → <WithComponents overrides={{ ChannelPreview: ChannelPreviewView }}>
  • ChannelPreview/ChannelPreviewMessenger → ChannelPreview/ChannelPreviewView

Before:

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

<ChannelList Preview={ChannelPreviewMessenger} />;

After:

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

<WithComponents overrides={{ ChannelPreview: ChannelPreviewView }}>
  <ChannelList />
</WithComponents>;

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 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

Prop Type Default Description
messageInputFloating boolean false Enables the floating message input layout
handleBlockUser function — Callback for blocking a user
reactionListType 'clustered' | 'segmented' 'clustered' Switches between clustered and segmented 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

Note: The component-type entries in this table (e.g., MessageReminderHeader, ReactionListClustered, FilePreview, UnsupportedAttachment, etc.) are now provided via <WithComponents overrides={{ ... }}> instead of as props on Channel. See Component Overrides: Props to WithComponents for the full migration pattern.

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', 'location', 'text'] 'text' now renders after 'attachments' and 'location'
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 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: 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
Card UrlPreview Renamed with new URLPreviewProps interface

Note on AudioAttachment: the source folder was moved from Attachment/AudioAttachment/ to Attachment/Audio/, but the public export name and the ComponentOverrides key are both still AudioAttachment. No code change is needed for direct imports or overrides referencing AudioAttachment.

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.

Sizing Behavior With Native Navigation Headers

If your chat screen renders below a native navigation header (@react-navigation/native-stack, Expo Router default headers, etc.), set both keyboardVerticalOffset and topInset on Channel to the same useHeaderHeight() value.

In V8 the optional react-native-keyboard-controller integration ran the app edge-to-edge, which made the attachment picker's internal windowHeight − topInset − sheetHeight − bottomInset math work without an explicit topInset. V9 no longer depends on react-native-keyboard-controller, so the header offset is no longer implicit — without topInset, the picker bottom sheet stops short of its full snap point.

const headerHeight = useHeaderHeight();

<Channel
  channel={channel}
  keyboardVerticalOffset={headerHeight}
  topInset={headerHeight}
>
  <MessageList />
  <MessageComposer />
</Channel>;

Screens with no native header (full-screen modals, custom JS headers rendered inside Channel) keep the default topInset={0}.

Removed:

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

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.

Removed Props

MessageOverlayBackground, ImageGalleryHeader, ImageGalleryFooter, ImageGalleryGrid, and ImageGalleryVideoControls have moved from OverlayProvider props to WithComponents overrides.

Removed Props

Removed Prop What to Use Instead
imageGalleryGridHandleHeight Removed
imageGalleryGridSnapPoints Removed
imageGalleryCustomComponents Replaced 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:

<WithComponents
  overrides={{
    ImageGalleryHeader: CustomHeader,
    ImageGalleryFooter: CustomFooter,
    ImageGalleryVideoControls: CustomVideoControls,
    ImageGalleryGrid: CustomGrid,
  }}
>
  <OverlayProvider>

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

Removed Notes
imageGalleryCustomComponents Replaced by WithComponents overrides
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 — overridable via <WithComponents>
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:

  • 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

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:

  • 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.

Warning:

MessageMenu and its MessageMenuProps type have been removed from the V9 public surface. The v9 overlay path does not render MessageMenu — the component is gone, not merely inert. If you customized MessageMenu in V8, that override will no longer type-check against v9 and will not render. Migrate that work to MessageReactionPicker, MessageActionList, MessageActionListItem, and MessageUserReactions instead, provided through <WithComponents overrides={{ ... }}>.

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: itemContainer → container, 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:

  • deleted — containerInner, deletedText, container
  • footer — container, name, editedText
  • bubbleWrapper, bubbleContentContainer, bubbleErrorContainer, bubbleReactionListTopContainer
  • savedForLaterHeader — container, label
  • reminderHeader — container, label, dot, time
  • sentToChannelHeader — container, label, dot, link
  • unsupportedAttachment — container, details, title
  • contentContainer, leftAlignItems, rightAlignItems
  • messageGroupedSingleStyles, 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 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:

  • reactionListItem — reactionCount, icon
  • reactionListClustered — contentContainer, reactionCount, iconStyle, icon
  • reactionListItemWrapper — 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, timeIcon
  • lowerRow, upperRow, statusContainer, titleContainer, wrapper
  • typingIndicatorPreview — container, text
  • messagePreview — container, 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: buttonContainer → contentContainer
  • 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

Component Overrides: Props to WithComponents

V9 fundamentally changes how you customize SDK components. In V8, component overrides were passed as props to Channel, ChannelList, Thread, Chat, and other SDK components. In V9, all component overrides are provided through a single <WithComponents> wrapper and read via the useComponentsContext() hook.

Why the Change

In V8, component override props were scattered across multiple contexts (MessagesContext, MessageInputContext, ChannelContext, ChannelsContext, etc.). This meant:

  • Channel alone accepted ~90 component override props, making the API surface large and hard to discover.
  • The same component could only be overridden at specific levels of the tree (e.g., you had to wrap Channel to override message components).
  • Adding a new overridable component required modifying context types, providers, and prop-forwarding logic in multiple places.

The new WithComponents pattern centralizes all overrides into a single context that can be placed at any level of the tree, supports nesting with automatic merging, and makes the full list of overridable components discoverable through a single ComponentOverrides type.

Basic Migration

V8:

<Channel
  channel={channel}
  Message={CustomMessage}
  SendButton={CustomSendButton}
  DateHeader={CustomDateHeader}
>
  <MessageList />
  <MessageComposer />
</Channel>

V9:

<WithComponents
  overrides={{
    Message: CustomMessage,
    SendButton: CustomSendButton,
    DateHeader: CustomDateHeader,
  }}
>
  <Channel channel={channel}>
    <MessageList />
    <MessageComposer />
  </Channel>
</WithComponents>

ChannelList Overrides

V8:

<ChannelList
  Preview={CustomPreview}
  EmptyStateIndicator={CustomEmpty}
  LoadingIndicator={CustomLoading}
/>

V9:

<WithComponents
  overrides={{
    Preview: CustomPreview,
    EmptyStateIndicator: CustomEmpty,
    LoadingIndicator: CustomLoading,
  }}
>
  <ChannelList />
</WithComponents>

Chat Component Overrides

V8:

<Chat client={client} LoadingIndicator={CustomLoading}>
  {/* ... */}
</Chat>

V9:

<WithComponents overrides={{ ChatLoadingIndicator: CustomLoading }}>
  <Chat client={client}>{/* ... */}</Chat>
</WithComponents>

Note: The Chat component's LoadingIndicator prop is now the ChatLoadingIndicator key in WithComponents to avoid naming conflicts with the general LoadingIndicator component.

Thread Component Overrides

V8:

<Thread MessageComposer={CustomThreadComposer} />

V9:

<WithComponents overrides={{ ThreadMessageComposer: CustomThreadComposer }}>
  <Thread />
</WithComponents>

Nesting Overrides

WithComponents supports nesting. Inner overrides merge over outer ones, so you can set app-wide defaults and then refine them per screen or per feature:

// App-wide overrides
<WithComponents overrides={{ Message: CustomMessage }}>
  <Chat client={client}>
    {/* Per-screen overrides (merge with parent) */}
    <WithComponents overrides={{ SendButton: CustomSendButton }}>
      <Channel channel={channel}>
        <MessageList />
        <MessageComposer />
      </Channel>
    </WithComponents>
  </Chat>
</WithComponents>

In this example, the Channel screen sees both Message: CustomMessage (from the outer provider) and SendButton: CustomSendButton (from the inner provider).

Reading Components in Custom Components

If your custom components read other components from context, update the hook:

V8:

const { MessageItemView } = useMessagesContext();
const { SendButton } = useMessageInputContext();
const { Preview } = useChannelsContext();

V9:

const { MessageItemView, SendButton, Preview } = useComponentsContext();

All component references are now in a single context, regardless of which part of the SDK they belong to.

Context Changes

The following contexts no longer include component keys — they now contain only data, configuration, and callback values:

Context Component Keys Moved to ComponentsContext
MessagesContextValue All ComponentType keys (e.g., Message, MessageItemView, MessageContent, Gallery, Attachment, Reply, etc.)
MessageInputContextValue All ComponentType keys (e.g., SendButton, InputButtons, AttachmentUploadPreviewList, etc.)
ChannelContextValue All ComponentType keys (e.g., EmptyStateIndicator, LoadingIndicator, NetworkDownIndicator, etc.)
ChannelsContextValue All ComponentType keys (e.g., Preview, Skeleton, EmptyStateIndicator, LoadingIndicator, etc.)

New exports:

Export Description
WithComponents Provider component — wraps children and merges overrides with parent scope
useComponentsContext Hook — returns all components with user overrides merged over SDK defaults
ComponentOverrides Type — Partial<...> of all overridable component keys for typing override objects

Removed Component Props

The following component override props are removed from their respective SDK components. Use the equivalent key in <WithComponents overrides={{ ... }}> instead.

Channel — All ~90 component override props removed, including but not limited to:

Attachment, AttachButton, AudioAttachment, AudioRecorder, AudioRecordingInProgress, AudioRecordingLockIndicator, AudioRecordingPreview, AudioRecordingWaveform, AutoCompleteSuggestionHeader, AutoCompleteSuggestionItem, AutoCompleteSuggestionList, CooldownTimer, DateHeader, EmptyStateIndicator, FileAttachment, FileAttachmentGroup, FileAttachmentIcon, FilePreview, Gallery, Giphy, ImageLoadingFailedIndicator, ImageLoadingIndicator, InlineDateSeparator, InlineUnreadIndicator, InputButtons, InputView, KeyboardCompatibleView, LoadingIndicator, Message, MessageActionList, MessageActionListItem, MessageAuthor, MessageBlocked, MessageBounce, MessageComposerLeadingView, MessageComposerTrailingView, MessageContent, MessageDeleted, MessageError, MessageFooter, MessageHeader, MessageInputFooterView, MessageInputHeaderView, MessageInputLeadingView, MessageInputTrailingView, MessageItemView, MessagePinnedHeader, MessageReactionPicker, MessageReminderHeader, MessageReplies, MessageRepliesAvatars, MessageSavedForLaterHeader, MessageStatus, MessageSwipeContent, MessageSystem, MessageTimestamp, MessageUserReactions, MessageUserReactionsAvatar, MessageUserReactionsItem, NetworkDownIndicator, ReactionListBottom, ReactionListClustered, ReactionListCountItem, ReactionListItem, ReactionListItemWrapper, ReactionListTop, Reply, ScrollToBottomButton, SendButton, SendMessageDisallowedIndicator, SentToChannelHeader, ShowThreadMessageInChannelButton, StickyHeader, StopMessageStreamingButton, StreamingMessageView, TypingIndicator, TypingIndicatorContainer, UnreadMessagesNotification, UnsupportedAttachment, UrlPreview, URLPreviewCompact, VideoThumbnail

ChannelList — Preview, EmptyStateIndicator, LoadingIndicator, LoadingErrorIndicator, Skeleton, HeaderErrorIndicator, HeaderNetworkDownIndicator, FooterLoadingIndicator, ListHeaderComponent

Chat — LoadingIndicator removed (use ChatLoadingIndicator key instead)

Thread — MessageComposer removed (use ThreadMessageComposer key instead)

Full List of Overridable Components

All keys available in <WithComponents overrides={{ ... }}>:

Key Default Component
Attachment Attachment
AttachButton AttachButton
AttachmentPickerContent AttachmentPickerContent
AttachmentPickerIOSSelectMorePhotos undefined
AttachmentPickerSelectionBar AttachmentPickerSelectionBar
AttachmentUploadIndicator AttachmentUploadIndicator
AttachmentUploadPreviewList AttachmentUploadPreviewList
AudioAttachment AudioAttachment
AudioAttachmentUploadPreview AudioAttachmentUploadPreview
AudioRecorder AudioRecorder
AudioRecordingInProgress AudioRecordingInProgress
AudioRecordingLockIndicator AudioRecordingLockIndicator
AudioRecordingPreview AudioRecordingPreview
AudioRecordingWaveform AudioRecordingWaveform
AutoCompleteSuggestionHeader AutoCompleteSuggestionHeader
AutoCompleteSuggestionItem AutoCompleteSuggestionItem
AutoCompleteSuggestionList AutoCompleteSuggestionList
ChannelDetailsBottomSheet ChannelDetailsBottomSheet
ChannelDetailsHeader ChannelDetailsHeader
ChannelListFooterLoadingIndicator ChannelListFooterLoadingIndicator
ChannelListHeaderErrorIndicator ChannelListHeaderErrorIndicator
ChannelListHeaderNetworkDownIndicator ChannelListHeaderNetworkDownIndicator
ChannelListLoadingIndicator ChannelListLoadingIndicator
ChannelPreview ChannelPreviewView
ChannelPreviewAvatar ChannelAvatar
ChannelPreviewLastMessage ChannelLastMessagePreview
ChannelPreviewMessage ChannelPreviewMessage
ChannelPreviewMessageDeliveryStatus ChannelMessagePreviewDeliveryStatus
ChannelPreviewMutedStatus ChannelPreviewMutedStatus
ChannelPreviewStatus ChannelPreviewStatus
ChannelPreviewTitle ChannelPreviewTitle
ChannelPreviewTypingIndicator ChannelPreviewTypingIndicator
ChannelPreviewUnreadCount ChannelPreviewUnreadCount
ChatLoadingIndicator undefined
CircularProgressIndicator CircularProgressIndicator
CooldownTimer CooldownTimer
CreatePollContent undefined
DateHeader DateHeader
EmptyStateIndicator EmptyStateIndicator
FileAttachment FileAttachment
FileAttachmentGroup FileAttachmentGroup
FileAttachmentIcon FileIcon
FileAttachmentUploadPreview FileAttachmentUploadPreview
FilePreview FilePreview
FileUploadInProgressIndicator FileUploadInProgressIndicator
FileUploadNotSupportedIndicator FileUploadNotSupportedIndicator
FileUploadRetryIndicator FileUploadRetryIndicator
Gallery Gallery
Giphy Giphy
ImageAttachmentUploadPreview ImageAttachmentUploadPreview
ImageComponent Image (React Native)
ImageGalleryFooter ImageGalleryFooter
ImageGalleryGrid ImageGalleryGrid
ImageGalleryHeader ImageGalleryHeader
ImageGalleryVideoControls ImageGalleryVideoControl
ImageLoadingFailedIndicator ImageLoadingFailedIndicator
ImageLoadingIndicator ImageLoadingIndicator
ImageOverlaySelectedComponent ImageOverlaySelectedComponent
ImageUploadInProgressIndicator ImageUploadInProgressIndicator
ImageUploadNotSupportedIndicator ImageUploadNotSupportedIndicator
ImageUploadRetryIndicator ImageUploadRetryIndicator
InlineDateSeparator InlineDateSeparator
InlineUnreadIndicator InlineUnreadIndicator
Input undefined
InputButtons InputButtons
InputView InputView
KeyboardCompatibleView KeyboardCompatibleView
ListHeaderComponent undefined
LoadingErrorIndicator LoadingErrorIndicator
MediaUploadProgressOverlay MediaUploadProgressOverlay
Message Message
MessageActionList MessageActionList
MessageActionListItem MessageActionListItem
MessageActions undefined
MessageAuthor MessageAuthor
MessageBlocked MessageBlocked
MessageBounce MessageBounce
MessageComposerLeadingView MessageComposerLeadingView
MessageComposerTrailingView MessageComposerTrailingView
MessageContent MessageContent
MessageContentBottomView undefined
MessageContentLeadingView undefined
MessageContentTopView undefined
MessageContentTrailingView undefined
MessageDeleted MessageDeleted
MessageError MessageError
MessageFooter MessageFooter
MessageHeader MessageHeader
MessageInputFooterView MessageInputFooterView
MessageInputHeaderView MessageInputHeaderView
MessageInputLeadingView MessageInputLeadingView
MessageInputTrailingView MessageInputTrailingView
MessageItemView MessageItemView
MessageList MessageList
MessageListLoadingIndicator LoadingIndicator
MessageLocation undefined
MessageOverlayBackground DefaultMessageOverlayBackground
MessagePinnedHeader MessagePinnedHeader
MessageReactionPicker MessageReactionPicker
MessageReminderHeader MessageReminderHeader
MessageReplies MessageReplies
MessageRepliesAvatars MessageRepliesAvatars
MessageSavedForLaterHeader MessageSavedForLaterHeader
MessageSpacer undefined
MessageStatus MessageStatus
MessageSwipeContent MessageSwipeContent
MessageSystem MessageSystem
MessageText undefined
MessageTimestamp MessageTimestamp
MessageUserReactions MessageUserReactions
MessageUserReactionsAvatar MessageUserReactionsAvatar
MessageUserReactionsItem MessageUserReactionsItem
NetworkDownIndicator NetworkDownIndicator
PollAllOptionsContent PollAllOptionsContent
PollAnswersListContent PollAnswersListContent
PollButtons PollButtons
PollContent undefined
PollHeader PollHeader
PollOptionFullResultsContent PollOptionFullResultsContent
PollResultsContent PollResultsContent
ReactionListBottom ReactionListBottom
ReactionListClustered ReactionListClustered
ReactionListCountItem ReactionListCountItem
ReactionListItem ReactionListItem
ReactionListItemWrapper ReactionListItemWrapper
ReactionListTop ReactionListTop
Reply Reply
ScrollToBottomButton ScrollToBottomButton
SendButton SendButton
SendMessageDisallowedIndicator SendMessageDisallowedIndicator
SentToChannelHeader SentToChannelHeader
ShowThreadMessageInChannelButton ShowThreadMessageInChannelButton
Skeleton Skeleton
StartAudioRecordingButton AudioRecordingButton
StickyHeader StickyHeader
StopMessageStreamingButton StopMessageStreamingButton
StreamingMessageView StreamingMessageView
ThreadListComponent ThreadListComponent
ThreadListEmptyPlaceholder ThreadListEmptyPlaceholder
ThreadListItem ThreadListItem
ThreadListItemMessagePreview ThreadListItemMessagePreview
ThreadListLoadingIndicator ThreadListLoadingIndicator
ThreadListLoadingMoreIndicator ThreadListLoadingNextIndicator
ThreadListUnreadBanner ThreadListUnreadBanner
ThreadMessageComposer MessageComposer
ThreadMessagePreviewDeliveryStatus ThreadMessagePreviewDeliveryStatus
TypingIndicator TypingIndicator
TypingIndicatorContainer TypingIndicatorContainer
UnreadMessagesNotification UnreadMessagesNotification
UnsupportedAttachment UnsupportedAttachment
UrlPreview URLPreview
URLPreviewCompact URLPreviewCompact
VideoAttachmentUploadPreview VideoAttachmentUploadPreview
VideoThumbnail VideoThumbnail

Keys with a default of undefined are optional extension points — they have no built-in UI but can be provided to enable custom behavior.


Migration Checklist

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

1. Install the New Dependency

yarn add react-native-teleport

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 → audioRecordingSendOnComplete 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 WithComponents overrides (ImageGalleryHeader, ImageGalleryFooter, ImageGalleryVideoControls, ImageGalleryGrid). MessageOverlayBackground is also now a WithComponents override instead of an OverlayProvider prop.

9. Update ChannelPreview Customizations

  • Replace latestMessagePreview with lastMessage in any custom ChannelPreviewStatus or ChannelPreviewMessage components.
  • Rename ChannelPreviewMessenger → ChannelPreviewView in any direct imports or prop types. Use <WithComponents overrides={{ ChannelPreview: ChannelPreviewView }}> instead of <ChannelList Preview={...} />.
  • Replace ChannelAvatar usage with the new ChannelAvatar from ui/Avatar/.
  • Rename useMutedUsers → useClientMutedUsers.
  • Keep the channelPreview theme namespace and channel-preview-button test ID unchanged.

10. Remove deletedMessagesVisibilityType

The deletedMessagesVisibilityType prop on Channel and its type DeletedMessagesVisibilityType have been removed. Deleted messages are now always included in the message list. Remove any usage of this prop — the sender, receiver, and never modes are no longer supported.

11. 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.

12. 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).

13. 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

14. 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

15. 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.

16. 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.

17. Update FlatList Types

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

18. Review messageContentOrder

The default order now renders 'text' last (after 'attachments' and 'location'). The position of 'text' in the array is respected, so pass a custom order to place it elsewhere — for example, the V8 arrangement with text before attachments:

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

19. Migrate Component Overrides to WithComponents

This is the most impactful structural change in V9. All component override props are removed from Channel, ChannelList, Chat, Thread, and other SDK components. Instead, wrap your tree with <WithComponents overrides={{ ... }}>:

  1. Find all component override props on Channel, ChannelList, Chat, and Thread in your codebase. Move each one into a <WithComponents overrides={{ ... }}> wrapper.
  2. Update context hook calls. Replace useMessagesContext(), useMessageInputContext(), useChannelContext(), or useChannelsContext() calls that read component keys with useComponentsContext().
  3. Handle renamed keys:
    • Chat's LoadingIndicator prop is now ChatLoadingIndicator in the overrides.
    • Thread's MessageComposer prop is now ThreadMessageComposer in the overrides.
  4. Place WithComponents at the appropriate level. You can nest multiple <WithComponents> wrappers — inner overrides merge over outer ones.

See Component Overrides: Props to WithComponents for full details and examples.

20. 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.

Add Chat to my app: getstream.io/SKILL.md

The fastest way to build with Stream. Start a new project or improve an existing one. Full CLI and documentation integration out of the box.


Ask your agent:

/stream Build me a Social App with Feeds and Moderation.
/stream Any livestream calls running?
/stream Chat React Native v9: <Your Question>