Upgrading to v9

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

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


React Native New Architecture Required

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

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

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


New Peer Dependency

V9 introduces one new required peer dependency:

PackageMinimum VersionRequired?Purpose
react-native-teleport>=0.5.4YesPortal-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

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.


Design Token System

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

The New theme/ Module

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

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

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

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

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

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

Color Changes

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

Semantic Token Renames

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

Previous TokenNew Token
backgroundCoreSurfacebackgroundCoreSurfaceDefault
badgeTextInversebadgeTextOnInverse
textInversetextOnInverse
backgroundCoreElevation4Removed with no direct replacement

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

Default Value Changes

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

New Base UI Component Library

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

Avatar

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

V8:

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

V9:

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

Button

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

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

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

Badge Components

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

Other New Primitives

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

MessageComposer — Complete Redesign

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

Architecture Change

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

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

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

New useMessageComposer Hook

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

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

New Slot Props

These composition slot components customize regions of the message input. Provide them via <WithComponents overrides={{ ... }}> (see Component Overrides: Props to WithComponents):

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

Removed Props

The following props are removed from Channel and MessageInputContext:

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

New Upload Indicator Components

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

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

OutputButtons Component

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

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

Component Path Changes

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

New MessageComposer Components

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

MessageInputContext Changes

Removed Values

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

Added Values

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

Modified Values

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

Voice recording send behavior

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

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

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

The default behavior also changed:

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

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


AudioAttachment Changes

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

Removed Props

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

Added Props

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

Hook Rename

V8 HookV9 Hook
useAudioControlleruseAudioRecorder
useAudioPlayerControluseAudioPlayer

MessageList Changes

New Props

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

Removed Props

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

Modified Props

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

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

New Sub-Components

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

MessagesContext Changes

Removed Values

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

Added Values

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

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., CardPropsURLPreviewProps, MessageSimplePropsMessageItemViewProps).

MessageSimpleMessageItemView

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

  • MessageSimpleMessageItemView
  • MessageSimplePropsMessageItemViewProps
  • MessageSimplePropsWithContextMessageItemViewPropsWithContext
  • <Channel MessageSimple={...} /><WithComponents overrides={{ MessageItemView: ... }}>
  • messagesContext.MessageSimpleuseComponentsContext().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.

MessageAvatarMessageAuthor

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

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

Before:

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

<Channel MessageAvatar={CustomMessageAvatar} />;

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

After:

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

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

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

MessageInputMessageComposer

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

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

Before:

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

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

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

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

After:

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

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

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

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

ChannelListMessengerChannelListView

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

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

Before:

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

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

<ChannelList List={ChannelListMessenger} />;

After:

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

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

ChannelPreviewMessengerChannelPreviewView

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

  • ChannelPreviewMessengerChannelPreviewView
  • ChannelPreviewMessengerPropsChannelPreviewViewProps
  • ChannelPreviewMessengerPropsWithContextChannelPreviewViewPropsWithContext
  • import { ChannelPreviewMessenger } from 'stream-chat-react-native'import { ChannelPreviewView } from 'stream-chat-react-native'
  • <ChannelList Preview={ChannelPreviewMessenger} /><WithComponents overrides={{ ChannelPreview: ChannelPreviewView }}>
  • ChannelPreview/ChannelPreviewMessengerChannelPreview/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 PropV9 Source
alignmentuseMessageContext()
messageuseMessageContext() or optional message prop
datemessage.created_at
isDeletedmessage.type === 'deleted'
lastGroupMessageuseMessageContext()
membersuseMessageContext()
MessageStatususeMessagesContext()
otherAttachmentsuseMessageContext()
showMessageStatususeMessageContext()

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

Swipe-to-reply boundary and width prop removal

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

The swipe gesture behavior itself is otherwise unchanged:

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

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

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

Before:

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

After:

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

Channel Component Changes

New Props

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

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

Modified Default Values

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

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


ChannelPreview Changes

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

Key API Change: latestMessagePreviewlastMessage

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

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

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

New Components

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

Removed Components

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

New Props

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

New Hooks

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

Hook Export Changes

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

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

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

Hook Rename

V8 HookV9 Hook
useMutedUsers (from Chat)useClientMutedUsers

Attachment Components

Renamed Components

V8 ComponentV9 ComponentNotes
CardUrlPreviewRenamed 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 ComponentNotes
AttachmentActionsRemoved — actions are now handled inline on attachments
ImageReloadIndicatorRemoved
AttachmentUnsupportedIndicatorRemoved

New Components

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

AttachmentPicker Overhaul

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

Removed:

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

Added:

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

OverlayProvider Changes

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

New Architecture

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

Removed Props

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

Removed Props

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

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

V8:

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

V9:

<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

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

Added

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

Message Components

New Components

ComponentDescription
MessageHeaderConsolidated message header — overridable via <WithComponents>
MessageBlockedUI for blocked messages
MessageSwipeContentContent revealed on message swipe
MessageTextContainerWrapper for message text with consistent styling
ReactionListClusteredClustered reaction list display (new default style)
ReactionListItemIndividual reaction item component
ReactionListItemWrapperWrapper for reaction items

Removed Components

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

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

MessagePreview → Hooks

The MessagePreview component is removed in favor of composable hooks:

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

State Store Additions

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

VideoPlayer

Mirrors the existing audio player architecture for video:

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

VideoPlayerPool

Pool management for multiple concurrent video players:

  • VideoPlayerPool class with VideoPlayerPoolState type

ImageGalleryStateStore

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

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

MessageOverlayStore

Complete state management for the portal-based overlay system:

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

New Hooks

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

Renamed Hooks

V8 HookV9 Hook
useAudioControlleruseAudioRecorder
useAudioPlayerControluseAudioPlayer
useMutedUsersuseClientMutedUsers

New Contexts

ContextDescription
BottomSheetContextProvides bottom sheet control (close() method)

Icons Changes

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

For public SDK consumers, the important changes are:

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

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

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

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

Key Renames and Direct Replacements

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

New Icons

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

Additional new public icon exports in V9 include:

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

Icons With The Same Component Name but Updated Artwork

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

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

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

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

Removed Icons

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

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

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

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

Bespoke / Internal Notes

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

MessageMenu (Context Menu)

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

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: itemContainercontainer, added wrapper
  • videoAttachmentUploadPreview: from { recorderIconContainer, recorderIcon, itemContainer, upload } to { durationContainer, durationText }

messageList Theme

Added keys:

  • inlineDateSeparatorContainer
  • unreadUnderlayContainer
  • scrollToBottomButtonContainer
  • stickyHeaderContainer
  • unreadMessagesNotificationContainer

Removed from scrollToBottomButton: touchable, wrapper, chevronColor

Modified unreadMessagesNotification:

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

messageItemView Theme

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

content:

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

New sub-components:

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

authorWrapper (renamed from avatarWrapper): Removed leftAlign, rightAlign

cardUrlPreview:

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

giphy:

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

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

New reaction list theme keys:

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

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

replies:

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

status:

  • Removed: readByCount, statusContainer
  • Added: container

Gallery defaults changed:

PropertyV8V9
gridHeight195192
maxHeight300192
minHeight100120
minWidth170120

channelPreview Theme

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

Added:

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

channelListSkeleton Theme

Completely restructured:

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

reply Theme

Completely redesigned:

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

threadListItem Theme

Completely restructured:

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

Other Theme Changes

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

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:

ContextComponent Keys Moved to ComponentsContext
MessagesContextValueAll ComponentType keys (e.g., Message, MessageItemView, MessageContent, Gallery, Attachment, Reply, etc.)
MessageInputContextValueAll ComponentType keys (e.g., SendButton, InputButtons, AttachmentUploadPreviewList, etc.)
ChannelContextValueAll ComponentType keys (e.g., EmptyStateIndicator, LoadingIndicator, NetworkDownIndicator, etc.)
ChannelsContextValueAll ComponentType keys (e.g., Preview, Skeleton, EmptyStateIndicator, LoadingIndicator, etc.)

New exports:

ExportDescription
WithComponentsProvider component — wraps children and merges overrides with parent scope
useComponentsContextHook — returns all components with user overrides merged over SDK defaults
ComponentOverridesType — 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

ChannelListPreview, EmptyStateIndicator, LoadingIndicator, LoadingErrorIndicator, Skeleton, HeaderErrorIndicator, HeaderNetworkDownIndicator, FooterLoadingIndicator, ListHeaderComponent

ChatLoadingIndicator removed (use ChatLoadingIndicator key instead)

ThreadMessageComposer removed (use ThreadMessageComposer key instead)

Full List of Overridable Components

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

KeyDefault Component
AttachmentAttachment
AttachButtonAttachButton
AttachmentPickerContentAttachmentPickerContent
AttachmentPickerIOSSelectMorePhotosundefined
AttachmentPickerSelectionBarAttachmentPickerSelectionBar
AttachmentUploadPreviewListAttachmentUploadPreviewList
AudioAttachmentAudioAttachment
AudioAttachmentUploadPreviewAudioAttachmentUploadPreview
AudioRecorderAudioRecorder
AudioRecordingInProgressAudioRecordingInProgress
AudioRecordingLockIndicatorAudioRecordingLockIndicator
AudioRecordingPreviewAudioRecordingPreview
AudioRecordingWaveformAudioRecordingWaveform
AutoCompleteSuggestionHeaderAutoCompleteSuggestionHeader
AutoCompleteSuggestionItemAutoCompleteSuggestionItem
AutoCompleteSuggestionListAutoCompleteSuggestionList
ChannelDetailsBottomSheetChannelDetailsBottomSheet
ChannelDetailsHeaderChannelDetailsHeader
ChannelListFooterLoadingIndicatorChannelListFooterLoadingIndicator
ChannelListHeaderErrorIndicatorChannelListHeaderErrorIndicator
ChannelListHeaderNetworkDownIndicatorChannelListHeaderNetworkDownIndicator
ChannelListLoadingIndicatorChannelListLoadingIndicator
ChannelPreviewChannelPreviewView
ChannelPreviewAvatarChannelAvatar
ChannelPreviewLastMessageChannelLastMessagePreview
ChannelPreviewMessageChannelPreviewMessage
ChannelPreviewMessageDeliveryStatusChannelMessagePreviewDeliveryStatus
ChannelPreviewMutedStatusChannelPreviewMutedStatus
ChannelPreviewStatusChannelPreviewStatus
ChannelPreviewTitleChannelPreviewTitle
ChannelPreviewTypingIndicatorChannelPreviewTypingIndicator
ChannelPreviewUnreadCountChannelPreviewUnreadCount
ChatLoadingIndicatorundefined
CooldownTimerCooldownTimer
CreatePollContentundefined
DateHeaderDateHeader
EmptyStateIndicatorEmptyStateIndicator
FileAttachmentFileAttachment
FileAttachmentGroupFileAttachmentGroup
FileAttachmentIconFileIcon
FileAttachmentUploadPreviewFileAttachmentUploadPreview
FilePreviewFilePreview
FileUploadInProgressIndicatorFileUploadInProgressIndicator
FileUploadNotSupportedIndicatorFileUploadNotSupportedIndicator
FileUploadRetryIndicatorFileUploadRetryIndicator
GalleryGallery
GiphyGiphy
ImageAttachmentUploadPreviewImageAttachmentUploadPreview
ImageComponentImage (React Native)
ImageGalleryFooterImageGalleryFooter
ImageGalleryGridImageGalleryGrid
ImageGalleryHeaderImageGalleryHeader
ImageGalleryVideoControlsImageGalleryVideoControl
ImageLoadingFailedIndicatorImageLoadingFailedIndicator
ImageLoadingIndicatorImageLoadingIndicator
ImageOverlaySelectedComponentImageOverlaySelectedComponent
ImageUploadInProgressIndicatorImageUploadInProgressIndicator
ImageUploadNotSupportedIndicatorImageUploadNotSupportedIndicator
ImageUploadRetryIndicatorImageUploadRetryIndicator
InlineDateSeparatorInlineDateSeparator
InlineUnreadIndicatorInlineUnreadIndicator
Inputundefined
InputButtonsInputButtons
InputViewInputView
KeyboardCompatibleViewKeyboardCompatibleView
ListHeaderComponentundefined
LoadingErrorIndicatorLoadingErrorIndicator
MessageMessage
MessageActionListMessageActionList
MessageActionListItemMessageActionListItem
MessageActionsundefined
MessageAuthorMessageAuthor
MessageBlockedMessageBlocked
MessageBounceMessageBounce
MessageComposerLeadingViewMessageComposerLeadingView
MessageComposerTrailingViewMessageComposerTrailingView
MessageContentMessageContent
MessageContentBottomViewundefined
MessageContentLeadingViewundefined
MessageContentTopViewundefined
MessageContentTrailingViewundefined
MessageDeletedMessageDeleted
MessageErrorMessageError
MessageFooterMessageFooter
MessageHeaderMessageHeader
MessageInputFooterViewMessageInputFooterView
MessageInputHeaderViewMessageInputHeaderView
MessageInputLeadingViewMessageInputLeadingView
MessageInputTrailingViewMessageInputTrailingView
MessageItemViewMessageItemView
MessageListMessageList
MessageListLoadingIndicatorLoadingIndicator
MessageLocationundefined
MessageOverlayBackgroundDefaultMessageOverlayBackground
MessagePinnedHeaderMessagePinnedHeader
MessageReactionPickerMessageReactionPicker
MessageReminderHeaderMessageReminderHeader
MessageRepliesMessageReplies
MessageRepliesAvatarsMessageRepliesAvatars
MessageSavedForLaterHeaderMessageSavedForLaterHeader
MessageSpacerundefined
MessageStatusMessageStatus
MessageSwipeContentMessageSwipeContent
MessageSystemMessageSystem
MessageTextundefined
MessageTimestampMessageTimestamp
MessageUserReactionsMessageUserReactions
MessageUserReactionsAvatarMessageUserReactionsAvatar
MessageUserReactionsItemMessageUserReactionsItem
NetworkDownIndicatorNetworkDownIndicator
PollAllOptionsContentPollAllOptionsContent
PollAnswersListContentPollAnswersListContent
PollButtonsPollButtons
PollContentundefined
PollHeaderPollHeader
PollOptionFullResultsContentPollOptionFullResultsContent
PollResultsContentPollResultsContent
ReactionListBottomReactionListBottom
ReactionListClusteredReactionListClustered
ReactionListCountItemReactionListCountItem
ReactionListItemReactionListItem
ReactionListItemWrapperReactionListItemWrapper
ReactionListTopReactionListTop
ReplyReply
ScrollToBottomButtonScrollToBottomButton
SendButtonSendButton
SendMessageDisallowedIndicatorSendMessageDisallowedIndicator
SentToChannelHeaderSentToChannelHeader
ShowThreadMessageInChannelButtonShowThreadMessageInChannelButton
SkeletonSkeleton
StartAudioRecordingButtonAudioRecordingButton
StickyHeaderStickyHeader
StopMessageStreamingButtonStopMessageStreamingButton
StreamingMessageViewStreamingMessageView
ThreadListComponentThreadListComponent
ThreadListEmptyPlaceholderThreadListEmptyPlaceholder
ThreadListItemThreadListItem
ThreadListItemMessagePreviewThreadListItemMessagePreview
ThreadListLoadingIndicatorThreadListLoadingIndicator
ThreadListLoadingMoreIndicatorThreadListLoadingNextIndicator
ThreadListUnreadBannerThreadListUnreadBanner
ThreadMessageComposerMessageComposer
ThreadMessagePreviewDeliveryStatusThreadMessagePreviewDeliveryStatus
TypingIndicatorTypingIndicator
TypingIndicatorContainerTypingIndicatorContainer
UnreadMessagesNotificationUnreadMessagesNotification
UnsupportedAttachmentUnsupportedAttachment
UrlPreviewURLPreview
URLPreviewCompactURLPreviewCompact
VideoAttachmentUploadPreviewVideoAttachmentUploadPreview
VideoThumbnailVideoThumbnail

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…
CommandsButtonInternal CommandChip (automatic)
MoreOptionsButtonInputButtons
InputEditingStateHeaderMessageInputHeaderView
InputReplyStateHeaderMessageInputHeaderView
toggleAttachmentPickeropenAttachmentPicker / closeAttachmentPicker
selectedPickeruseAttachmentPickerState() hook

4. Update Attachment Picker Customizations

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

5. Update Upload Progress Indicators

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

6. Update URL Preview Customizations

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

7. Update Audio Hooks and Props

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

Replace the imageGalleryCustomComponents nested object on OverlayProvider with 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 ChannelPreviewMessengerChannelPreviewView 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 useMutedUsersuseClientMutedUsers.
  • 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 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']}
>

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.