# MessageComposer

`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 or sends. [Message composer UI](https://getstream.io/chat/docs/sdk/react/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/state/writing-messages/message-composer/) explains the object.

## Best practices

- Render it inside a `Channel` or a `Thread`. It writes into that thread's or channel's composer, and throws outside both.
- Change what is sent in the state layer, not in the component: transform the message with [composer middleware](https://getstream.io/chat/docs/sdk/react/state/writing-messages/composer-middleware/), or replace the request with a `sendMessageRequest` handler, as [Customizing requests](https://getstream.io/chat/docs/sdk/react/state/writing-messages/message-operations/#customizing-requests) shows.
- Change the look with `WithComponents`. Scope the override around one `MessageComposer` when only that composer should differ.
- To tell a thread composer from a channel one in your own parts, read `useMessageComposerController().threadId`. It's set only for a thread's composer.

## Basic usage

```tsx
import {
  Channel,
  ChannelHeader,
  MessageComposer,
  MessageList,
  Thread,
  ThreadHeader,
} from "stream-chat-react";

export const ChannelPane = () => (
  <Channel channel={channel}>
    <ChannelHeader />
    <MessageList />
    <MessageComposer audioRecordingEnabled />
  </Channel>
);

export const ThreadPane = () => (
  <Thread thread={thread}>
    <ThreadHeader />
    <MessageList />
    <MessageComposer focus />
  </Thread>
);
```

The first composer writes into `channel.messageComposer`, the second into `thread.messageComposer`.

## UI customization

`MessageComposer` renders the component registered as `MessageComposerUI`, and provides [`MessageComposerContext`](https://getstream.io/chat/docs/sdk/react/component-reference/message-composer/messagecomposercontext/) to it. The default `MessageComposerUI` is built from parts, each with an override key of its own:

- To replace one part, such as the send button or the attachment selector, register it under its key. [Message composer UI](https://getstream.io/chat/docs/sdk/react/ui/components/message-composer-ui/#replacing-parts) lists the keys, and [Composer UI components](https://getstream.io/chat/docs/sdk/react/component-reference/message-composer/composer-ui-components/) has their props.
- To replace the whole layout, register your own `MessageComposerUI`. See [Input UI](https://getstream.io/chat/docs/sdk/react/component-reference/message-composer/input-ui/).

When unmounted, `MessageComposer` saves a draft of the composer if drafts are turned on, and then clears the thread's or channel's composer. See [Drafts](https://getstream.io/chat/docs/sdk/react/features/messaging/drafts/).

## Supplying a composer

By default, `MessageComposer` writes into the composer of the thread or channel it's rendered in. Wrap it in `MessageComposerControllerProvider` to have it write into a composer you create instead, for example one that edits a message where it sits. Every `MessageComposer` and composer hook below the provider uses the supplied composer.

A supplied composer belongs to you:

- When `MessageComposer` unmounts, it saves the composer's draft if drafts are on for it, but it never clears the composer. Clear it yourself when the composition ends.
- Submitting works as for any composer: `MessageComposer.update()` while a message is being edited, `MessageComposer.send()` otherwise.

### Edit a message inline

An edit composer has the message as its composition context. This one shows a `MessageComposer` for the message while the edit is in progress:

```tsx
import { useState } from "react";
import type { LocalMessage, MessageComposerState } from "stream-chat";
import { MessageComposer as MessageComposerController } from "stream-chat";
import {
  MessageComposer,
  MessageComposerControllerProvider,
  useChatContext,
  useStateStore,
} from "stream-chat-react";

const editingSelector = (state: MessageComposerState) => ({
  isEditing: state.editedMessage !== null,
});

export const InlineEditor = ({ message }: { message: LocalMessage }) => {
  const { client } = useChatContext();
  const [composer] = useState(
    () =>
      new MessageComposerController({
        client,
        composition: message,
        compositionContext: message,
      }),
  );
  const { isEditing } = useStateStore(composer.state, editingSelector);

  if (!isEditing) return null;

  return (
    <MessageComposerControllerProvider messageComposerController={composer}>
      <MessageComposer />
    </MessageComposerControllerProvider>
  );
};
```

Saving the edit, or cancelling it with the ✕ on the edit preview, empties the composer, and the editor disappears. [Editing a message in an edit composer](https://getstream.io/chat/docs/sdk/react/features/messaging/sending-editing-and-deleting/#editing-a-message-in-an-edit-composer) shows where to render it and how to open it from the Edit action.

## Props

| Prop                            | Type                                                                                                          | Default                                  | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `additionalTextareaProps`       | `Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "defaultValue" \| "style" \| "disabled" \| "value">` | none                                     | Props for the `<textarea>` that `<TextareaComposer/>` renders. A `placeholder` here replaces "Send a message". `className` is added to the textarea's classes. `onPaste` runs before the composer's paste handling; see [TextareaComposer props](https://getstream.io/chat/docs/sdk/react/component-reference/message-composer/composer-ui-components/#textareacomposer-props). `<TextareaComposer/>` sets its own `onBlur`, `onChange`, `onCompositionStart`, `onCompositionEnd`, `onKeyDown`, `onScroll`, `onSelect` and `disabled`, and its `aria-activedescendant`, `aria-autocomplete`, `aria-controls` and `aria-label`, so those are ignored here. |
| `asyncMessagesMultiSendEnabled` | `boolean`                                                                                                     | `false`                                  | Keeps a finished voice recording in the composer, to send with the rest of the message. Without it, the recording is sent as soon as the user finishes it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `audioRecordingConfig`          | `CustomAudioRecordingConfig`                                                                                  | none                                     | Settings for the voice recorder. See [Audio Recorder](https://getstream.io/chat/docs/sdk/react/component-reference/voice-recording/audio-recorder/). A new object with the same values keeps the recorder; [Encoding recordings as MP3](https://getstream.io/chat/docs/sdk/react/features/rich-content/voice-recordings-and-audio-playback/#encoding-recordings-as-mp3) says when it's replaced.                                                                                                                                                                                                                                                                              |
| `audioRecordingEnabled`         | `boolean`                                                                                                     | `false`                                  | Shows the record button while the composer is empty, nothing is being edited, no command is active and the browser can record.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `focus`                         | `boolean`                                                                                                     | `false`                                  | Focuses the input on mount, and again whenever the attachments or the quoted message change.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `generateRecordingTitle`        | `(mimeType: string) => string`                                                                                | `audio_recording_<ISO date>.<extension>` | Names the file of a voice recording from the type of the final file: the encoder's output, such as `audio/wav`, or Safari's recording as is. See [Recording file names](https://getstream.io/chat/docs/sdk/react/features/rich-content/voice-recordings-and-audio-playback/#recording-file-names).                                                                                                                                                                                                                                                                                                                                                        |
| `hideSendButton`                | `boolean`                                                                                                     | `false`                                  | Removes the control after the input: the send button, the cooldown timer and the record button. While an AI reply is being generated and the stop button isn't turned off, the control still shows.                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `maxRows`                       | `number`                                                                                                      | `10`                                     | The most rows the input grows to.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `minRows`                       | `number`                                                                                                      | none                                     | The fewest rows the input shows. Without it, an empty input is one row high.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `shouldSubmit`                  | `(event: React.KeyboardEvent<HTMLTextAreaElement>) => boolean`                                                | Enter without Shift                      | Decides which key press submits. The default submits on Enter without Shift, and not while an input method is composing text.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |

`<MessageComposer/>` puts these props into [`MessageComposerContext`](https://getstream.io/chat/docs/sdk/react/component-reference/message-composer/messagecomposercontext/), where its parts read them. `audioRecordingConfig` and `generateRecordingTitle` are the exception: they set up the voice recorder, and the context doesn't carry them.

## Hooks

- [`useMessageComposerController`](https://getstream.io/chat/docs/sdk/react/reference/hooks/composer-hooks/#usemessagecomposercontroller): the composer a component writes into.
- [`useMessageComposerContext`](https://getstream.io/chat/docs/sdk/react/reference/hooks/composer-hooks/#usemessagecomposercontext): the props above and the input's bindings.
- [`useMessageComposerSubmitFn`](https://getstream.io/chat/docs/sdk/react/reference/hooks/composer-hooks/#usemessagecomposersubmitfn): submits the composition the way the send button does.

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

---

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