# Composer UI components

> **Info:** This is release candidate documentation for **Stream Chat React Native SDK v10**. For the latest stable version, see the [latest version (v9)](https://getstream.io/chat/docs/sdk/react-native/).

The default [`MessageComposer`](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/messagecomposer/) is built from the parts on this page. Each part reads the composer from `useMessageComposer()`, so it works in a channel, in a thread and while a message is being edited. [Message composer UI](https://getstream.io/chat/docs/sdk/react-native/v10/ui/components/message-composer-ui/) explains what each part shows and which store it reads.

## Best practices

- Replace a part by registering it under its override key with `WithComponents`, so the default layout keeps the rest.
- Render the parts inside a `Channel` or a `Thread`. They read `MessageInputContext` and the composer from them, and throw outside both.
- Keep the SDK's text input in a custom layout, by rendering `InputView` or `AutoCompleteInput`, so typing, mentions and commands keep reaching the composer.
- To keep a default part and add to it, register a component that renders the exported part with your additions around it.
- Declare override objects once, at module scope. `WithComponents` reads them when it mounts.

## Available building blocks

The parts below `MessageComposer`, in the order the row renders them:

| Component                          | Override key                     | What it renders                                                                                                                                                          | Props                                                                                                       |
| ---------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `MessageComposerLeadingView`       | `MessageComposerLeadingView`     | The area before the input box, with `InputButtons`. Hidden from the start of a recording until it's sent or deleted                                                      | none                                                                                                        |
| `InputButtons`                     | none                             | `AttachButton`, when a picker is available, the user has `upload-file` and no command is active                                                                          | [Below](#inputbuttons-props)                                                                                |
| `AttachButton`                     | `AttachButton`                   | The attach button, which opens or closes the [attachment picker](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/attachment-picker/)                | [Below](#attachbutton-props)                                                                                |
| `MessageInputHeaderView`           | `MessageInputHeaderView`         | The previews above the input: the message being edited or quoted, the attachments and the link previews. Hidden from the start of a recording until it's sent or deleted | none                                                                                                        |
| `Reply`                            | `Reply`                          | The edited message, with `mode="edit"`, or the quoted message, with `mode="reply"`, and a close button. Also quoted messages in message bubbles                          | [Message UI components](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message/message-ui-components/) |
| `AttachmentUploadPreviewList`      | `AttachmentUploadPreviewList`    | The composer's attachments: audio files and voice recordings stacked, the others in a horizontal row                                                                     | none                                                                                                        |
| `ImageAttachmentUploadPreview`     | `ImageAttachmentUploadPreview`   | An image, as a thumbnail with a remove button                                                                                                                            | [Below](#upload-preview-props)                                                                              |
| not exported                       | `VideoAttachmentUploadPreview`   | A video, as a thumbnail with its duration, or as a file row without a thumbnail                                                                                          | [Below](#upload-preview-props)                                                                              |
| `FileAttachmentUploadPreview`      | `FileAttachmentUploadPreview`    | A file, as a row with its icon, name and size. Also audio files when no audio package is installed                                                                       | [Below](#upload-preview-props)                                                                              |
| `AudioAttachmentUploadPreview`     | `AudioAttachmentUploadPreview`   | An audio file or a voice recording, as a player                                                                                                                          | [Below](#upload-preview-props)                                                                              |
| `InputView`                        | `InputView`                      | The command chip, `AutoCompleteInput` and `ShowThreadMessageInChannelButton`                                                                                             | [Below](#inputview-props)                                                                                   |
| `AutoCompleteInput`                | none                             | The text input. It writes the text and the cursor into `textComposer`, which opens the suggestions                                                                       | [Below](#inputview-props)                                                                                   |
| `ShowThreadMessageInChannelButton` | none                             | The "Also send to channel" checkbox, inside a `Thread` while `allowThreadMessagesInChannel` is `true`                                                                    | [Below](#showthreadmessageinchannelbutton-props)                                                            |
| `AutoCompleteSuggestionList`       | `AutoCompleteSuggestionList`     | The suggestions for `@`, `/` and `:`. The message list renders it, above the composer                                                                                    | none                                                                                                        |
| `AutoCompleteSuggestionHeader`     | `AutoCompleteSuggestionHeader`   | The list's header: "Instant Commands" for commands, and the emoji query for emoji. Nothing for mentions                                                                  | [Below](#suggestion-props)                                                                                  |
| `AutoCompleteSuggestionItem`       | `AutoCompleteSuggestionItem`     | One suggestion. Pressing it calls `TextComposer.handleSelect()`                                                                                                          | [Below](#suggestion-props)                                                                                  |
| `MentionSuggestionItem`            | `MentionSuggestionItem`          | The content of an `@` suggestion, by its `mentionType`: a user, `@channel` or `@here`, a role or a user group                                                            | the `MentionSuggestion`'s fields                                                                            |
| `MessageInputTrailingView`         | `MessageInputTrailingView`       | `OutputButtons`, unless a recording is locked or stopped                                                                                                                 | none                                                                                                        |
| `OutputButtons`                    | none                             | One control, the first that applies: the stop button, the edit button, the cooldown timer, the record button, the send button                                            | [Below](#outputbuttons-props)                                                                               |
| `StopMessageStreamingButton`       | `StopMessageStreamingButton`     | The button that stops an AI reply, while the AI is thinking or generating                                                                                                | [Below](#stopmessagestreamingbutton-props)                                                                  |
| `CooldownTimer`                    | `CooldownTimer`                  | The seconds left of a slow mode cooldown, on a disabled button                                                                                                           | none                                                                                                        |
| `AudioRecordingButton`             | `StartAudioRecordingButton`      | The record button                                                                                                                                                        | [Audio recorder](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/audio-recorder/)      |
| `SendButton`                       | `SendButton`                     | The send button                                                                                                                                                          | [Below](#sendbutton-props)                                                                                  |
| `MessageComposerTrailingView`      | `MessageComposerTrailingView`    | Nothing. Rendered after the input box, for a part of your own                                                                                                            | none                                                                                                        |
| `SendMessageDisallowedIndicator`   | `SendMessageDisallowedIndicator` | "You can't send messages in this channel", in place of the composer when the user lacks `send-message`                                                                   | none                                                                                                        |

The edit button, which shows a checkmark while a message is being edited or a command is active, and the command chip have no override key and aren't exported. Replace `MessageInputTrailingView` to change the edit button, and `InputView` to change the command chip. `LinkPreviewList` isn't exported either: replace `MessageInputHeaderView` to change the link previews.

`WithComponents` also accepts the keys `InputButtons`, `ShowThreadMessageInChannelButton`, `MessageInputLeadingView` and `MessageInputFooterView`, but no component reads them, so registering them changes nothing. To change the parts they name, replace the part that renders them: `MessageComposerLeadingView` for `InputButtons`, and `InputView` for `ShowThreadMessageInChannelButton`. The exported `MessageInputLeadingView` and `MessageInputFooterView` render nothing, and the default layout doesn't render them.

The composer's recorder parts are on [Audio recorder](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/audio-recorder/), and the attachment picker's on [Attachment picker](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/attachment-picker/). The `Input` key replaces the whole row; see [What it renders](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/messagecomposer/#what-it-renders).

## InputButtons props

`InputButtons` reads its props from `MessageInputContext` and the user's capabilities. Pass one to override it. It renders `AttachButton` when one of `hasCameraPicker`, `hasFilePicker` and `hasImagePicker` is `true`, `uploadFile` is `true` and no command is active. Otherwise it renders nothing.

| Prop              | Type                   | Default                      | Description                                       |
| ----------------- | ---------------------- | ---------------------------- | ------------------------------------------------- |
| `hasCameraPicker` | `boolean`              | from `MessageInputContext`   | Whether the camera picker is available.           |
| `hasFilePicker`   | `boolean`              | from `MessageInputContext`   | Whether the file picker is available.             |
| `hasImagePicker`  | `boolean`              | from `MessageInputContext`   | Whether the image picker is available.            |
| `hasCommands`     | `boolean`              | from `MessageInputContext`   | Whether the channel has commands.                 |
| `uploadFile`      | `boolean \| undefined` | the `upload-file` capability | Whether the user can upload files in the channel. |

## AttachButton props

`AttachButton` reads its props from `MessageInputContext` and the attachment picker. Pass one to override it.

| Prop                      | Type                                                      | Default                                      | Description                                                                                                                                                                       |
| ------------------------- | --------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `handleOnPress`           | `((event: GestureResponderEvent) => void) & (() => void)` | none                                         | Called on press, in place of `handleAttachButtonPress` and `toggleAttachmentPicker`. It's called without an event.                                                                |
| `handleAttachButtonPress` | `() => void`                                              | from `MessageInputContext`                   | Called on press, in place of `toggleAttachmentPicker`, when `handleOnPress` isn't set.                                                                                            |
| `toggleAttachmentPicker`  | `() => void`                                              | opens or closes the picker                   | Called on press otherwise. The default opens the picker, or, while it's open, focuses the input when `focusInputOnPickerClose` is `true` and closes the picker when it's `false`. |
| `isAttachmentPickerOpen`  | `boolean`                                                 | whether a picker source is selected, `false` | Switches the button's accessibility label between adding an attachment and closing the attachments.                                                                               |
| `disabled`                | `boolean`                                                 | `false`                                      | Disables the button.                                                                                                                                                              |
| `disableAttachmentPicker` | `boolean`                                                 | from the attachment picker context           | Not read by the button.                                                                                                                                                           |

## InputView props

`InputView` passes its props to `AutoCompleteInput`, which renders the `TextInput`. `MessageComposer` passes `TextInputComponent` and the `additionalTextInputProps` it has. Both take every `TextInput` prop and these:

| Prop                       | Type                            | Default                    | Description                                                                                              |
| -------------------------- | ------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------- |
| `TextInputComponent`       | `TextInputOverrideComponent`    | React Native's `TextInput` | The component that renders the input. It must forward its ref.                                           |
| `cooldownRemainingSeconds` | `number`                        | `useCooldownRemaining()`   | The seconds left of a slow mode cooldown. While it's set, the placeholder reads "Slow mode, wait 5s...". |
| `setInputBoxRef`           | `Ref<InputBoxRef> \| undefined` | from `MessageInputContext` | Receives the input, extended with `clearState()` and `restoreState(text)`.                               |
| `channel`                  | `Channel`                       | from `ChannelContext`      | Compared to skip re-renders; not passed to the `TextInput`.                                              |
| `t`                        | `StreamTFunction`               | from `TranslationContext`  | Translates the placeholders.                                                                             |

The `TextInput` props are spread after the SDK's own, so they replace them:

- `onChangeText`, `onSelectionChange` and `value` replace the SDK's handling. With your own `onChangeText`, typing doesn't reach the composer.
- `placeholder` replaces "Send a message", a command's placeholder, such as "Search GIFs" for `/giphy`, and the slow mode placeholder.
- `style` replaces the SDK's style of the input, instead of adding to it.
- `numberOfLines` sets how many lines the input grows to before it scrolls, `5` by default.

The input is not editable while the composer's `text.enabled` setting is `false`, and its `maxLength` is the composer's `text.maxLengthOnSend`.

## ShowThreadMessageInChannelButton props

`ShowThreadMessageInChannelButton` reads its props from `ThreadContext`. It renders only while `thread` is set and `allowThreadMessagesInChannel` is `true`. Pressing it toggles `showReplyInChannel` in the composer's state with `MessageComposer.toggleShowReplyInChannel()`.

| Prop                           | Type              | Default                   | Description                                                            |
| ------------------------------ | ----------------- | ------------------------- | ---------------------------------------------------------------------- |
| `thread`                       | `Thread`          | from `ThreadContext`      | The thread the composer is in. Without one, the checkbox doesn't show. |
| `allowThreadMessagesInChannel` | `boolean`         | from `ThreadContext`      | The `Thread` prop, `true` by default. `false` hides the checkbox.      |
| `t`                            | `StreamTFunction` | from `TranslationContext` | Translates the label.                                                  |

## Suggestion props

`AutoCompleteSuggestionList` takes no props. It reads `textComposer.state.suggestions`, renders the items of its search source, loads more of them at the end of the list, and announces the list to screen readers when it opens. It renders the two parts below with these props.

`AutoCompleteSuggestionHeader`:

| Prop          | Type     | Default | Description                                                     |
| ------------- | -------- | ------- | --------------------------------------------------------------- |
| `queryText`   | `string` | none    | The text typed after the trigger.                               |
| `triggerType` | `string` | none    | `"mention"` for `@`, `"command"` for `/` and `"emoji"` for `:`. |

`AutoCompleteSuggestionItem`:

| Prop          | Type                     | Default  | Description                                                                                                                               |
| ------------- | ------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `itemProps`   | `TextComposerSuggestion` | required | The suggestion. Pressing the item passes it to `TextComposer.handleSelect()`.                                                             |
| `triggerType` | `string`                 | none     | Picks the content: `MentionSuggestionItem` for `"mention"`, `EmojiSuggestionItem` for `"emoji"`, `CommandSuggestionItem` for `"command"`. |

`MentionSuggestionItem` gets the suggestion's fields as props. `EmojiSuggestionItem` and `CommandSuggestionItem` are exported too, and have no override key: replace `AutoCompleteSuggestionItem` to change them. [Mentions](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/mentions/) and [Suggestions](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/suggestions/) explain where the suggestions come from.

## Upload preview props

`AttachmentUploadPreviewList` takes no props. It renders a preview per attachment, by type, with these props. With `audioRecordingSendOnComplete`, voice recordings get no preview, because they're sent at once.

| Prop                | Type                                                                                         | Default  | Description                                                                                          |
| ------------------- | -------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `attachment`        | `A`                                                                                          | required | The attachment, of the preview's type, with its `localMetadata`: `id`, `file`, `uploadState`.        |
| `handleRetry`       | `(attachment: LocalUploadAttachment) => void \| Promise<LocalUploadAttachment \| undefined>` | required | Uploads the attachment again. The list passes the composer's `AttachmentManager.uploadAttachment()`. |
| `removeAttachments` | `(ids: string[]) => void`                                                                    | required | Removes attachments from the composer by their `localMetadata.id`.                                   |

`ImageAttachmentUploadPreviewProps`, `FileAttachmentUploadPreviewProps` and `AudioAttachmentUploadPreviewProps` are exported, each with `A` set to the attachment types it previews. The default video preview renders the SDK's image preview, not your `ImageAttachmentUploadPreview`, so replace `VideoAttachmentUploadPreview` to change videos too.

### Upload state indicators

The previews show an attachment's upload state with these parts, each an override key. Which one shows depends on the attachment's `uploadState` and on the composer's `attachments.pendingUploadsEnabled` setting, which is off by default and which `Chat` turns on with `enableOfflineSupport`:

| `uploadState` | With pending uploads on     | With pending uploads off (the default) |
| ------------- | --------------------------- | -------------------------------------- |
| `uploading`   | none                        | the in-progress indicator              |
| `failed`      | none                        | the retry indicator                    |
| `blocked`     | the not-supported indicator | the not-supported indicator            |

With pending uploads on, the message is sent while its attachments upload, and the message list shows their progress instead; see [Sending with pending uploads](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/sending-with-pending-uploads/). An image or video preview also shows `ImageLoadingIndicator` while its thumbnail loads.

| Component                          | Props                                                                             | What it renders                                                                                                                                                                            |
| ---------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `FileUploadInProgressIndicator`    | `localId?: string`, `sourceUrl?: string`, `totalBytes?: number \| string \| null` | While `client.uploadManager` has the upload, the `AttachmentUploadIndicator` override, a progress ring or a spinner while the progress is unknown, and the megabytes sent of `totalBytes`. |
| `FileUploadRetryIndicator`         | `onPress: () => void`                                                             | "Network error" and a "Retry Upload" button that calls `onPress`.                                                                                                                          |
| `FileUploadNotSupportedIndicator`  | `localMetadata: LocalAttachmentUploadMetadata`                                    | "File too large" when the upload check failed on the size limit, "Not supported" otherwise.                                                                                                |
| `ImageUploadInProgressIndicator`   | `localId?: string`, `sourceUrl?: string`, `totalBytes?: number \| string \| null` | The `AttachmentUploadIndicator` override over the thumbnail. `totalBytes` isn't read.                                                                                                      |
| `ImageUploadRetryIndicator`        | `onRetryHandler: () => void`                                                      | A retry badge that calls `onRetryHandler`.                                                                                                                                                 |
| `ImageUploadNotSupportedIndicator` | none                                                                              | An error mark in the thumbnail's corner.                                                                                                                                                   |

The file and image versions of the in-progress indicator take `UploadInProgressIndicatorProps`, and the others `FileUploadRetryIndicatorProps`, `FileUploadNotSupportedIndicatorProps` and `ImageUploadRetryIndicatorProps`.

`AttachmentUploadIndicator` draws the progress with two more override keys, which the attachments of a message sent with pending uploads use too. For a file, it renders `CircularProgressIndicator`, 16 points wide, while the progress is known, and an `ActivityIndicator` while it isn't. For an image or a video, it renders `MediaUploadProgressOverlay`, which covers the thumbnail and draws a `CircularProgressIndicator`, 28 points wide with a stroke of 3, or an `ActivityIndicator`, in its center.

`CircularProgressIndicator` takes `CircularProgressIndicatorProps`:

| Prop              | Type                   | Default  | Description                                                                                                                           |
| ----------------- | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `progress`        | `number`               | required | The progress as a percentage, clamped to 0 to 100. The ring animates to a new value. `NaN` draws a spinning arc instead of the ring.  |
| `filledColor`     | `ColorValue`           | required | The color of the part of the ring that's done. The SDK passes `accentPrimary`.                                                        |
| `unfilledColor`   | `ColorValue`           | required | The color of the rest of the ring. The SDK passes `borderCoreDefault`.                                                                |
| `backgroundColor` | `ColorValue`           | required | The fill inside the ring. The SDK passes `backgroundCoreElevation0`.                                                                  |
| `size`            | `number`               | `16`     | The width and height, in points.                                                                                                      |
| `strokeWidth`     | `number`               | `2`      | The width of the ring.                                                                                                                |
| `style`           | `StyleProp<ViewStyle>` |          | The style of the ring's view. The SDK passes `messageItemView.attachmentUploadIndicator.indicator` from the theme inside the overlay. |
| `testID`          | `string`               |          | The test ID of the ring's view.                                                                                                       |

`MediaUploadProgressOverlay` takes `MediaUploadProgressOverlayProps`:

| Prop          | Type     | Default | Description                                                                            |
| ------------- | -------- | ------- | -------------------------------------------------------------------------------------- |
| `progress`    | `number` |         | The progress as a percentage. Without it, the overlay shows an `ActivityIndicator`.    |
| `size`        | `number` | `18`    | The size passed to `CircularProgressIndicator`. `AttachmentUploadIndicator` passes 28. |
| `strokeWidth` | `number` | `3`     | The stroke width passed to `CircularProgressIndicator`.                                |
| `testID`      | `string` |         | Gives the overlay's view this test ID with `-content` appended.                        |

The overlay's background is the `backgroundCoreOverlayLight` token, and `messageItemView.attachmentUploadIndicator.overlayContent` in the theme styles it. It doesn't take touches, so the thumbnail under it stays pressable.

## OutputButtons props

`OutputButtons` reads its props from `MessageInputContext`, the channel and the composer. Pass one to override it. It renders, in this order, the first control that applies:

1. `StopMessageStreamingButton`, while the channel's AI state is thinking or generating. It calls `Channel.stopAIResponse()`.
2. The edit button, while a message is being edited or a command is active. It's disabled while the composition is empty.
3. `CooldownTimer`, while `cooldownIsActive` is `true`.
4. `StartAudioRecordingButton`, when `audioRecordingEnabled` is `true` and the composer has no text and no attachments.
5. `SendButton`, otherwise. It's disabled until the composer has something to send.

| Prop                                 | Type      | Default                              | Description                                                                                                                                                     |
| ------------------------------------ | --------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channel`                            | `Channel` | from `ChannelContext`                | The channel whose AI state is read and whose reply is stopped.                                                                                                  |
| `cooldownIsActive`                   | `boolean` | whether a slow mode cooldown runs    | Shows `CooldownTimer`.                                                                                                                                          |
| `audioRecordingEnabled`              | `boolean` | from `MessageInputContext`           | Shows the record button while the composer is empty.                                                                                                            |
| `hasAttachments`                     | `boolean` | whether the composer has attachments | Hides the record button.                                                                                                                                        |
| `isOnline`                           | `boolean` | `useSettledWSConnectionHealth()`     | Disables the send button while a command is active and it's `false`. The edit button replaces the send button while a command is active, so this has no effect. |
| `asyncMessagesLockDistance`          | `number`  | from `MessageInputContext`           | Not read.                                                                                                                                                       |
| `asyncMessagesMinimumPressDuration`  | `number`  | from `MessageInputContext`           | Not read.                                                                                                                                                       |
| `asyncMessagesSlideToCancelDistance` | `number`  | from `MessageInputContext`           | Not read.                                                                                                                                                       |
| `audioRecordingSendOnComplete`       | `boolean` | from `MessageInputContext`           | Not read.                                                                                                                                                       |

## SendButton props

| Prop          | Type                  | Default                    | Description                                                                                                                                                                                  |
| ------------- | --------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `disabled`    | `boolean`             | required                   | Disables the button. `OutputButtons` always passes it. A replacement rendered without it is treated as enabled.                                                                              |
| `sendMessage` | `() => Promise<void>` | from `MessageInputContext` | Called on press. The default sends the composition, or saves the edit; see [MessageInputContext](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/messageinputcontext/). |

## StopMessageStreamingButton props

| Prop      | Type         | Default | Description                                                                               |
| --------- | ------------ | ------- | ----------------------------------------------------------------------------------------- |
| `onPress` | `() => void` | none    | Called on press. `OutputButtons` passes a function that calls `Channel.stopAIResponse()`. |

[SDK integration](https://getstream.io/chat/docs/sdk/react-native/v10/features/ai/sdk-integration/) explains the AI state.

## Example

This send button keeps the default and adds a counter of the characters typed. It registers as `SendButton`, so it shows in the same cases as the default:

```tsx title="src/SendButtonWithCount.tsx"
import { StyleSheet, Text, View } from "react-native";
import type { TextComposerState } from "stream-chat";
import type { SendButtonProps } from "stream-chat-react-native";
import {
  SendButton,
  useMessageComposer,
  useStateStore,
  WithComponents,
} from "stream-chat-react-native";
import type { PropsWithChildren } from "react";

const textLengthSelector = ({ text }: TextComposerState) => ({
  length: text.length,
});

const SendButtonWithCount = (props: SendButtonProps) => {
  const { textComposer } = useMessageComposer();
  const { length } = useStateStore(textComposer.state, textLengthSelector);

  return (
    <View style={styles.row}>
      {length ? <Text style={styles.count}>{length}</Text> : null}
      <SendButton {...props} />
    </View>
  );
};

const overrides = { SendButton: SendButtonWithCount };

export const ComposerOverrides = ({ children }: PropsWithChildren) => (
  <WithComponents overrides={overrides}>{children}</WithComponents>
);

const styles = StyleSheet.create({
  count: { fontSize: 12 },
  row: { alignItems: "center", flexDirection: "row", gap: 4 },
});
```

Wrap the `Channel` and `Thread` screens in `ComposerOverrides`. The [Custom composer UI cookbook](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/composer/custom-composer/) builds a whole composer row of its own.

## Hooks

- [`useMessageComposer`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#usemessagecomposer): the composer the parts write into.
- [`useAttachmentManagerState`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#useattachmentmanagerstate): the attachments and whether more fit.
- [`useMessageComposerHasSendableData`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#usemessagecomposerhassendabledata): whether the send button is enabled.
- [`usePendingUploadsEnabled`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#usependinguploadsenabled): whether the composer sends before uploads finish.
- [`useCooldownRemaining`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/channel-and-thread-hooks/#usecooldownremaining): the seconds left of a slow mode cooldown.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/composer-ui-components/](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/composer-ui-components/).