# MessageComposer

> **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/).

`MessageComposer` renders the message input of a channel or a thread. The message being written lives in a `MessageComposer` object from `stream-chat`, such as `channel.messageComposer`. The component renders that object and calls it when the user types, attaches, records or sends. [Message composer UI](https://getstream.io/chat/docs/sdk/react-native/v10/ui/components/message-composer-ui/) explains the parts it renders and which composer it writes into, and [Message composer](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/message-composer/) explains the object.

## Best practices

- Render it inside a `Channel` or a `Thread`. It reads the channel, the composer and its settings from them, and throws outside both.
- Configure the composer on `Channel` and `Thread`, not on `MessageComposer`. Props such as `audioRecordingEnabled`, `hasCommands` and `messageInputFloating` reach the composer's parts and the message list through `MessageInputContext`. Most of the props `MessageComposer` accepts don't, as the [Props](#props) tables show.
- Change what is sent in the state layer: transform the message with [composer middleware](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/composer-middleware/), or replace the request with a `sendMessageRequest` handler, as [Customizing requests](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/message-operations/#customizing-requests) shows.
- Change the look with `WithComponents`. Each part of the composer has an override key, listed on [Composer UI components](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/composer-ui-components/).
- Declare `additionalTextInputProps` once, at module scope or in `useMemo`. `MessageComposer` re-renders when the object changes.

## Basic usage

```tsx title="src/ChatScreens.tsx"
import type {
  Channel as StreamChannel,
  Thread as StreamThread,
} from "stream-chat";
import {
  Channel,
  MessageComposer,
  MessageList,
  Thread,
} from "stream-chat-react-native";

export const ChannelScreen = ({ channel }: { channel: StreamChannel }) => (
  <Channel audioRecordingEnabled channel={channel}>
    <MessageList />
    <MessageComposer />
  </Channel>
);

export const ThreadScreen = ({ thread }: { thread: StreamThread }) => (
  <Thread audioRecordingEnabled thread={thread}>
    <MessageList />
    <MessageComposer />
  </Thread>
);
```

The first composer writes into `channel.messageComposer`, the second into `thread.messageComposer`. Voice recording is turned on with a prop of `Channel` and of `Thread`, because neither passes its configuration to the other.

## What it renders

`MessageComposer` renders the composer row: `MessageComposerLeadingView` with the attach button, then a box with `MessageInputHeaderView` (the previews), `InputView` (the command chip, the text input and the also-send-to-channel checkbox) and `MessageInputTrailingView` (the send button or its replacement), then `MessageComposerTrailingView`. While the user records a voice message, the recorder parts take the input's place. Each of these is an override key; [Composer UI components](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/composer-ui-components/) lists them with their props, and [Audio recorder](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/audio-recorder/) covers the recorder.

It also renders:

- in place of everything above, `SendMessageDisallowedIndicator`, once the channel's capabilities are known and the user lacks `send-message`, unless a message is being edited;
- the default poll creation dialog, `CreatePoll` in a `PollModal`, while `showPollCreationDialog` is `true` (see [Poll components](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/poll-components/));
- the `Input` override in place of the row, when you register one. It gets `additionalTextInputProps` and `getUsers()`, which returns the users of `members` and `watchers` without duplicates.

When it mounts inside a `Thread`, it loads the thread's saved draft into the thread's composer, when drafts are turned on. When it unmounts, it closes the attachment picker. It doesn't save or clear the composer: see [Drafts](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/drafts/).

With `messageInputFloating`, the row floats over the message list, with a shadow, instead of sitting below it. The message list reads the same setting from `MessageInputContext` to leave room for it, so set it on `Channel` or `Thread`.

## Props

All props are optional. Without a prop, `MessageComposer` uses the value it reads from its context or computes itself, which the Default column names.

| Prop                                 | Type                                   | Default                              | Description                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------------------ | -------------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `additionalTextInputProps`           | `TextInputProps`                       | the `Channel` or `Thread` prop       | Props for the React Native `TextInput`, also passed to the `Input` override. They're spread after the SDK's own, so `onChangeText`, `onSelectionChange`, `value` and `style` replace the SDK's handling and styles. A `placeholder` replaces "Send a message", a command's placeholder and the slow mode placeholder. `numberOfLines` sets how many lines the input grows to, `5` by default. |
| `TextInputComponent`                 | `TextInputOverrideComponent`           | React Native's `TextInput`           | The text input component. It gets the same props as the default and must forward its ref.                                                                                                                                                                                                                                                                                                     |
| `closeAttachmentPicker`              | `() => void`                           | from `MessageInputContext`           | Called when `MessageComposer` unmounts.                                                                                                                                                                                                                                                                                                                                                       |
| `showPollCreationDialog`             | `boolean`                              | from `MessageInputContext`           | Whether the default poll creation dialog shows.                                                                                                                                                                                                                                                                                                                                               |
| `closePollCreationDialog`            | `() => void`                           | from `MessageInputContext`           | Closes the default poll creation dialog. Passed to `CreatePoll` and to `PollModal`'s `onRequestClose`.                                                                                                                                                                                                                                                                                        |
| `createPollOptionGap`                | `number`                               | none                                 | Passed to `CreatePoll`: the gap between the options of the poll form, `8` when unset. `MessageComposer` doesn't read the `Channel` or `Thread` prop of the same name from `MessageInputContext`.                                                                                                                                                                                              |
| `sendMessage`                        | `() => Promise<void>`                  | from `MessageInputContext`           | Passed to `CreatePoll`, which calls it after creating the poll. The send button doesn't use this prop: it reads `sendMessage` from `MessageInputContext`.                                                                                                                                                                                                                                     |
| `messageInputFloating`               | `boolean`                              | from `MessageInputContext`, `false`  | Floats the row over the message list. See [What it renders](#what-it-renders).                                                                                                                                                                                                                                                                                                                |
| `messageInputHeightStore`            | `MessageInputHeightStore`              | from `MessageInputContext`           | The store the row writes its height into. The message list reads the context's store, so replacing it here leaves the list without the height.                                                                                                                                                                                                                                                |
| `asyncMessagesLockDistance`          | `number`                               | from `MessageInputContext`, `50`     | Here only the lock indicator's slide animation reads it. The record button's gesture reads the context's value, so set it on `Channel` or `Thread`.                                                                                                                                                                                                                                           |
| `asyncMessagesSlideToCancelDistance` | `number`                               | from `MessageInputContext`, `75`     | Here only the fade of "Slide to Cancel" reads it. The record button's gesture reads the context's value, so set it on `Channel` or `Thread`.                                                                                                                                                                                                                                                  |
| `inputBoxRef`                        | `React.RefObject<InputBoxRef \| null>` | from `MessageInputContext`           | Read to style the focused input box and to move screen reader focus to the input on mount. The text input fills the context's ref, not one passed here.                                                                                                                                                                                                                                       |
| `channel`                            | `Channel`                              | the channel of `Channel` or `Thread` | Read only for `getUsers()` of the `Input` override, when `members` or `watchers` isn't set.                                                                                                                                                                                                                                                                                                   |
| `members`                            | `MembersState['members']`              | `channel.state` `members`            | The members `getUsers()` returns, for the `Input` override.                                                                                                                                                                                                                                                                                                                                   |
| `watchers`                           | `ChannelWatchState['watchers']`        | `channel.state` `watchers`           | The watchers `getUsers()` returns, for the `Input` override.                                                                                                                                                                                                                                                                                                                                  |
| `isKeyboardVisible`                  | `boolean`                              | `useKeyboardVisibility()`            | Whether the keyboard is open. The row's bottom padding and its place over an open attachment picker depend on it. Meant for the SDK's own use.                                                                                                                                                                                                                                                |
| `isRecordingStateIdle`               | `boolean`                              | the recorder's state                 | Whether no recording is in progress. While it's `false`, `AudioRecorder` takes the input's place. Meant for the SDK's own use.                                                                                                                                                                                                                                                                |
| `micLocked`                          | `boolean`                              | the recorder's state                 | Whether the recording is locked. Shows `AudioRecordingInProgress` above the input. Meant for the SDK's own use.                                                                                                                                                                                                                                                                               |
| `recordingStatus`                    | `string`                               | the recorder's state                 | `"idle"`, `"recording"` or `"stopped"`. `"stopped"` shows `AudioRecordingPreview` above the input. Meant for the SDK's own use.                                                                                                                                                                                                                                                               |

### Props that have no effect

`MessageComposer` accepts these props, but none of its parts reads them from `MessageComposer`. The parts read the values from their contexts instead. Set the configuration ones on `Channel` or `Thread`.

| Prop                                | Type                                                                       | Where the parts read it                             |
| ----------------------------------- | -------------------------------------------------------------------------- | --------------------------------------------------- |
| `audioRecordingEnabled`             | `boolean`                                                                  | `MessageInputContext`, set on `Channel` or `Thread` |
| `audioRecordingSendOnComplete`      | `boolean`                                                                  | `MessageInputContext`, set on `Channel` or `Thread` |
| `asyncMessagesMinimumPressDuration` | `number`                                                                   | `MessageInputContext`, set on `Channel` or `Thread` |
| `compressImageQuality`              | `number`                                                                   | `MessageInputContext`, set on `Channel` or `Thread` |
| `openPollCreationDialog`            | `({ sendMessage }: Pick<LocalMessageInputContext, 'sendMessage'>) => void` | `MessageInputContext`, set on `Channel` or `Thread` |
| `audioRecorderManager`              | `AudioRecorderManager`                                                     | `MessageInputContext`                               |
| `uploadNewFile`                     | `(file: File) => Promise<void>`                                            | `MessageInputContext`                               |
| `clearEditingState`                 | `() => void`                                                               | `MessageComposerAPIContext`                         |
| `editing`                           | `boolean`                                                                  | the composer's `editedMessage`                      |
| `isOnline`                          | `boolean`                                                                  | `useSettledWSConnectionHealth()`                    |
| `t`                                 | `StreamTFunction`                                                          | `TranslationContext`                                |

A change to `audioRecordingEnabled`, `audioRecordingSendOnComplete`, `asyncMessagesMinimumPressDuration`, `openPollCreationDialog`, `editing`, `isOnline` or `t` still re-renders `MessageComposer`.

## Examples

### Change the text input

This composer capitalizes sentences and shows a placeholder of its own. The props are declared once, at module scope:

```tsx title="src/ChannelScreen.tsx"
import type { TextInputProps } from "react-native";
import type { Channel as StreamChannel } from "stream-chat";
import {
  Channel,
  MessageComposer,
  MessageList,
} from "stream-chat-react-native";

const textInputProps: TextInputProps = {
  autoCapitalize: "sentences",
  placeholder: "Write to the team",
};

export const ChannelScreen = ({ channel }: { channel: StreamChannel }) => (
  <Channel channel={channel}>
    <MessageList />
    <MessageComposer additionalTextInputProps={textInputProps} />
  </Channel>
);
```

The placeholder replaces the command and slow mode placeholders too. To keep them, set `additionalTextInputProps` without a `placeholder`. To set the same props for every composer under a `Channel` or `Thread`, pass `additionalTextInputProps` to it instead.

### Focus the input

`inputBoxRef` in `MessageInputContext` holds the text input. A part rendered inside the composer, or anywhere under the `Channel` or `Thread`, focuses it:

```tsx title="src/FocusComposerButton.tsx"
import { Pressable, Text } from "react-native";
import { useMessageInputContext } from "stream-chat-react-native";

export const FocusComposerButton = () => {
  const { inputBoxRef } = useMessageInputContext();

  return (
    <Pressable onPress={() => inputBoxRef.current?.focus()}>
      <Text>Reply</Text>
    </Pressable>
  );
};
```

## Hooks

- [`useMessageComposer`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#usemessagecomposer): the composer a component writes into.
- [`useMessageInputContext`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#usemessageinputcontext): the composer's settings, the send function, the pickers and the recorder. The values are on [MessageInputContext](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/messageinputcontext/).
- [`useMessageComposerHasSendableData`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#usemessagecomposerhassendabledata): whether the send button is enabled.

[Composer hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/) lists the rest.

---

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