# 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](https://reactnative.dev/docs/new-architecture-intro) 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 |

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

- [V8 troubleshooting: Incorrect input position](/chat/docs/sdk/react-native/basics/troubleshooting/#incorrect-input-position)

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:

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

```tsx
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:**

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

**V9:**

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

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

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

```tsx
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](#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](#new-upload-indicator-components)) |
| `cooldownEndsAt`                          | Cooldown is now managed internally by `OutputButtons`                                          |
| `selectedPicker`                          | Removed from context — use `useAttachmentPickerState()` hook                                   |
| `toggleAttachmentPicker`                  | Replaced by `openAttachmentPicker` / `closeAttachmentPicker`                                   |

### New Upload Indicator Components

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

| Component                          | Purpose                               |
| ---------------------------------- | ------------------------------------- |
| `FileUploadInProgressIndicator`    | Progress indicator for file uploads   |
| `FileUploadRetryIndicator`         | Retry button for failed file uploads  |
| `FileUploadNotSupportedIndicator`  | Indicator for unsupported file types  |
| `ImageUploadInProgressIndicator`   | Progress indicator for image uploads  |
| `ImageUploadRetryIndicator`        | Retry button for failed image uploads |
| `ImageUploadNotSupportedIndicator` | Indicator for unsupported images      |

### OutputButtons Component

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

| Priority | Condition                                    | Button Shown                 |
| -------- | -------------------------------------------- | ---------------------------- |
| 1        | AI streaming is active                       | `StopMessageStreamingButton` |
| 2        | User is editing a message or command         | `EditButton`                 |
| 3        | Slow-mode cooldown is active                 | `CooldownTimer`              |
| 4        | Audio recording enabled, no text/attachments | `StartAudioRecordingButton`  |
| 5        | Default                                      | `SendButton`                 |

### Component Path Changes

| V8 Path                                    | V9 Path                                                                 |
| ------------------------------------------ | ----------------------------------------------------------------------- |
| `MessageInput/AttachButton`                | `MessageInput/components/InputButtons/AttachButton`                     |
| `MessageInput/SendButton`                  | `MessageInput/components/OutputButtons/SendButton`                      |
| `MessageInput/CooldownTimer`               | `MessageInput/components/OutputButtons/CooldownTimer`                   |
| `MessageInput/InputButtons`                | `MessageInput/components/InputButtons`                                  |
| `MessageInput/AttachmentUploadPreviewList` | `MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList` |

### New MessageComposer Components

| Component            | Description                                              |
| -------------------- | -------------------------------------------------------- |
| `CommandChip`        | Displays the active slash command with a dismiss button  |
| `EditButton`         | Confirm button shown when editing a message              |
| `LinkPreviewList`    | Renders URL preview cards inline in the input area       |
| `MicPositionContext` | Context provider for audio recording microphone position |

---

## MessageInputContext Changes

### Removed Values

| Removed Value                     | What to Use Instead                                      |
| --------------------------------- | -------------------------------------------------------- |
| `asyncMessagesMultiSendEnabled`   | `audioRecordingSendOnComplete` with inverted semantics   |
| `cooldownEndsAt`                  | Managed internally in `OutputButtons`                    |
| `selectedPicker`                  | `useAttachmentPickerState()` hook                        |
| `toggleAttachmentPicker`          | `openAttachmentPicker` / `closeAttachmentPicker`         |
| `getMessagesGroupStyles`          | Removed — grouping logic is now internal                 |
| `legacyImageViewerSwipeBehaviour` | Removed — the legacy image viewer is no longer supported |

### Added Values

| New Value                      | Type                                         | Description                                             |
| ------------------------------ | -------------------------------------------- | ------------------------------------------------------- |
| `audioRecordingSendOnComplete` | `boolean`                                    | Whether a completed voice recording is sent immediately |
| `audioRecorderManager`         | `AudioRecorderManager`                       | Manages audio recording lifecycle and state             |
| `startVoiceRecording`          | `() => Promise<boolean>`                     | Start a voice recording                                 |
| `stopVoiceRecording`           | `() => Promise<void>`                        | Stop the current voice recording                        |
| `deleteVoiceRecording`         | `() => Promise<void>`                        | Delete the current voice recording                      |
| `uploadVoiceRecording`         | `(sendOnComplete: boolean) => Promise<void>` | Upload the recorded voice message                       |
| `messageInputFloating`         | `boolean`                                    | Whether the input is using floating layout              |
| `messageInputHeightStore`      | `MessageInputHeightStore`                    | State store for tracking input height                   |
| `createPollOptionGap`          | `number`                                     | Gap between poll options in creation UI                 |

### Modified Values

| Value                          | V8 Type                                           | V9 Type                                            |
| ------------------------------ | ------------------------------------------------- | -------------------------------------------------- |
| `takeAndUploadImage`           | `() => Promise<void>`                             | `() => Promise<{askToOpenSettings?; canceled?}>`   |
| `CooldownTimer`                | `ComponentType<CooldownTimerProps>`               | `ComponentType` (no props — self-contained)        |
| `AudioRecordingPreview`        | `ComponentType<AudioRecordingPreviewProps>`       | `ComponentType` (no props — self-contained)        |
| `VideoAttachmentUploadPreview` | `ComponentType<FileAttachmentUploadPreviewProps>` | `ComponentType<VideoAttachmentUploadPreviewProps>` |

### Voice recording send behavior

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

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

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

The default behavior also changed:

- Before: `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](#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:

```tsx
<Channel MessageSimple={CustomMessageSimple} />;

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

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

After:

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

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

<Channel MessageAvatar={CustomMessageAvatar} />;

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

After:

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

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

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

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

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

After:

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

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

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

<ChannelList List={ChannelListMessenger} />;
```

After:

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

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

<ChannelList Preview={ChannelPreviewMessenger} />;
```

After:

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

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

After:

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

---

## Channel Component Changes

### New Props

| Prop                                 | Type                       | Default       | Description                                               |
| ------------------------------------ | -------------------------- | ------------- | --------------------------------------------------------- |
| `messageInputFloating`               | `boolean`                  | `false`       | Enables the floating message input layout                 |
| `handleBlockUser`                    | `function`                 | —             | Callback for blocking a user                              |
| `reactionListType`                   | `'clustered' \| 'default'` | `'clustered'` | Switches between clustered and default reaction lists     |
| `urlPreviewType`                     | `'full' \| 'compact'`      | `'full'`      | Controls URL preview display style                        |
| `onAlsoSentToChannelHeaderPress`     | `function`                 | —             | Press handler for thread "also sent to channel" header    |
| `asyncMessagesLockDistance`          | `number`                   | `50`          | Audio recording gesture: lock distance threshold          |
| `asyncMessagesMinimumPressDuration`  | `number`                   | `500`         | Audio recording gesture: minimum press duration (ms)      |
| `audioRecordingSendOnComplete`       | `boolean`                  | `true`        | Whether a completed voice recording is sent immediately   |
| `asyncMessagesSlideToCancelDistance` | `number`                   | `75`          | Audio recording gesture: slide-to-cancel distance         |
| `createPollOptionGap`                | `number`                   | —             | Gap between poll options in the creation UI               |
| `MessageReminderHeader`              | Component                  | —             | Custom header for message reminders                       |
| `MessageSavedForLaterHeader`         | Component                  | —             | Custom header for saved messages                          |
| `SentToChannelHeader`                | Component                  | —             | Custom header for "also sent to channel" thread messages  |
| `ReactionListClustered`              | Component                  | —             | Custom clustered reaction list                            |
| `ReactionListCountItem`              | Component                  | —             | Custom reaction count item                                |
| `ReactionListItem`                   | Component                  | —             | Custom reaction list item                                 |
| `ReactionListItemWrapper`            | Component                  | —             | Custom reaction list item wrapper                         |
| `URLPreviewCompact`                  | Component                  | —             | Custom compact URL preview                                |
| `FilePreview`                        | Component                  | —             | Custom file preview component                             |
| `UnsupportedAttachment`              | Component                  | —             | Fallback renderer for unsupported/custom attachment types |

> **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](#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](#removed-props) |

### Modified Default Values

| Prop                                    | V8 Default                                                                                   | V9 Default                                                                                   | Impact                                                |
| --------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| `messageContentOrder`                   | `['quoted_reply', 'gallery', 'files', 'poll', 'ai_text', 'text', 'attachments', 'location']` | `['quoted_reply', 'gallery', 'files', 'poll', 'ai_text', 'attachments', 'text', 'location']` | `'attachments'` and `'text'` are swapped              |
| `numberOfAttachmentImagesToLoadPerCall` | `60`                                                                                         | `25`                                                                                         | Reduced for better performance on lower-end devices   |
| `attachmentPickerBottomSheetHeight`     | `vh(45)` (viewport-based)                                                                    | `disableAttachmentPicker ? 72 : 333` (fixed)                                                 | Switched from viewport-relative to fixed pixel values |
| `attachmentSelectionBarHeight`          | `52`                                                                                         | `72`                                                                                         | Increased for better touch targets                    |

> **Action required:** If your UI depends on `'text'` rendering before `'attachments'`, explicitly pass the V8 order array 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:

```tsx
// 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:

```tsx
// 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.

**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`](/chat/docs/sdk/react-native/customization/custom_components/) overrides.

### Removed Props

| Removed Prop                   | What to Use Instead                            |
| ------------------------------ | ---------------------------------------------- |
| `imageGalleryGridHandleHeight` | Removed                                        |
| `imageGalleryGridSnapPoints`   | Removed                                        |
| `imageGalleryCustomComponents` | Replaced by direct component props (see below) |

### Image Gallery Customization — Flattened

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

**V8:**

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

**V9:**

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

```tsx
// 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.

<admonition type="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={{ ... }}>`.
</admonition>

### ActionType Union

The `ActionType` union has been expanded with `blockUser`:

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

```typescript
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:**

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

**V9:**

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

### ChannelList Overrides

**V8:**

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

**V9:**

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

### Chat Component Overrides

**V8:**

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

**V9:**

```tsx
<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:**

```tsx
<Thread MessageComposer={CustomThreadComposer} />
```

**V9:**

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

```tsx
// 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:**

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

**V9:**

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

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

### 8. Update Image Gallery Customizations

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](#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](#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:

```tsx
<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](#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.


---

This page was last updated at 2026-04-17T17:33:47.169Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/basics/upgrading-from-v8/](https://getstream.io/chat/docs/sdk/react-native/basics/upgrading-from-v8/).