Input UI

The input UI is the component MessageComposer renders inside MessageComposerContext: the whole visible composer. The default is <MessageComposerUI/>, and you replace it with the MessageComposerUI override key. Message composer UI explains its parts and what each one reads.

Best practices

  • Replace one part before you replace the whole layout. Each part has its own override key; see Composer UI components.
  • Build a custom layout from the exported parts. They read the composer themselves, so suggestions, uploads and commands keep working.
  • Scope WithComponents around one MessageComposer when only that composer should look different.
  • Keep the str-chat__message-composer* class names if you want the default styling.

Basic usage

Register your layout as MessageComposerUI. MessageComposer still provides the composer and the context:

import {
  Channel,
  MessageComposer,
  MessageList,
  SendButton,
  TextareaComposer,
  useMessageComposerSubmitFn,
  WithComponents,
} from "stream-chat-react";

const CompactComposerUI = () => {
  const submit = useMessageComposerSubmitFn();

  return (
    <div className="compact-composer">
      <TextareaComposer />
      <SendButton sendMessage={submit} />
    </div>
  );
};

const overrides = { MessageComposerUI: CompactComposerUI };

export const ChannelWithCompactComposer = () => (
  <Channel channel={channel}>
    <MessageList />
    <WithComponents overrides={overrides}>
      <MessageComposer />
    </WithComponents>
  </Channel>
);

The send button and the Enter key both submit through useMessageComposerSubmitFn(): MessageComposer.update() while a message is being edited, MessageComposer.send() otherwise.

UI customization

The default <MessageComposerUI/> renders, in order:

  1. <WithDragAndDropUpload/> around everything, so files dropped on the composer are uploaded into it.
  2. While a voice recording is in progress, <AudioRecorder/> in place of all the rest.
  3. <AttachmentSelector/>. While the composer edits a message (it renders inside that message's MessageContext), <SimpleAttachmentSelector/> instead: a plain upload button, shown under the same rule as the attachment selector's File action. Attachment Selector explains the File action and <SimpleAttachmentSelector/>.
  4. <MessageComposerPreviews/>: the previews of what the message will carry, from its preview set. Previews lists the default set and how to change it.
  5. The controls:
    • <CommandChip/>, while a command is selected;
    • TextareaComposer;
    • SendToChannelCheckbox, in a thread;
    • <AdditionalMessageComposerActions/>, which holds the EmojiPicker override;
    • <MessageComposerActions/>: the first that applies of the stop button for an AI reply, the cooldown timer, the record button and the send button.

Every part except WithDragAndDropUpload and MessageComposerActions is read from an override key, so a replacement registered with WithComponents shows in the default layout too.

A custom layout can keep the same structure. This one leaves out the command chip, the also-send-to-channel checkbox and the emoji picker:

import {
  AttachmentSelector,
  AudioRecorder,
  MessageComposer,
  MessageComposerActions,
  MessageComposerPreviews,
  TextareaComposer,
  useMessageComposerContext,
  WithComponents,
  WithDragAndDropUpload,
} from "stream-chat-react";

const CustomComposerUI = () => {
  const { recordingController } = useMessageComposerContext();

  return (
    <WithDragAndDropUpload
      className="str-chat__message-composer-container"
      component="div"
    >
      <div className="str-chat__message-composer">
        {recordingController.recordingState ? (
          <AudioRecorder />
        ) : (
          <>
            <AttachmentSelector />
            <div className="str-chat__message-composer-compose-area">
              <MessageComposerPreviews />
              <div className="str-chat__message-composer-controls">
                <TextareaComposer />
                <MessageComposerActions />
              </div>
            </div>
          </>
        )}
      </div>
    </WithDragAndDropUpload>
  );
};

const overrides = { MessageComposerUI: CustomComposerUI };

export const ComposerWithCustomLayout = () => (
  <WithComponents overrides={overrides}>
    <MessageComposer audioRecordingEnabled />
  </WithComponents>
);

Without the command chip, an active command shows nowhere: the composer takes the command's name out of the text, and sends it in front of the text. To keep the command in the text instead, remove the command middlewares, as A UI without a command chip shows.

SendToChannelCheckbox and StopAIGenerationButton aren't exported, so a custom layout can't render their defaults. MessageComposerActions still renders the default stop button. When a command is selected, the default layout also adds the str-chat__message-composer--command-active class; see Command-state behavior.

The Custom composer recipe builds a complete layout.

Props

The input UI receives no props. It reads what it needs from:

  • useMessageComposerContext(): the MessageComposer props, the textarea ref and the recorder;
  • useMessageComposerController(): the composer and its stores;
  • the composer hooks, such as useMessageComposerSubmitFn() and useAttachmentManagerState().