Info:
This is release candidate documentation for Stream Chat React SDK v15. For the latest stable version, see the latest version (v14).

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 explains the parts it renders and which composer it writes into, and 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, or replace the request with a sendMessageRequest handler, as 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

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 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 lists the keys, and Composer UI components has their props.
  • To replace the whole layout, register your own MessageComposerUI. See 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.

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:

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 place 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". The input sets its own className, onBlur, onChange, onKeyDown, onPaste, onScroll and onSelect, 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.
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.
emojiSearchIndex EmojiSearchIndex the override Put into MessageComposerContext, falling back to the emojiSearchIndex key of WithComponents. No built-in component reads it. Emoji autocomplete comes from a middleware; see Emoji.
focus boolean false Focuses the input on mount, and again whenever the attachments or the quoted message change.
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.
parent LocalMessage none Put into MessageComposerContext for your own parts. The SDK doesn't read it: inside a Thread, the composer is the thread's.
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, where its parts read them.

Hooks

Composer hooks lists the rest.