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

Message composer UI

The message composer UI is where a user writes, edits and sends a message. The message being written lives in a MessageComposer object from stream-chat: its text, attachments, quoted message, poll and location. The UI renders that object and calls it when the user acts. This page covers which composer the UI drives, the parts of the UI and what each part reads. The props are in the MessageComposer reference, and the object is on Message composer.

The default message composer

Which composer it drives

<MessageComposer /> and every part inside it work on the composer that useMessageComposerController() returns. The hook picks the first of:

  1. a composer supplied with MessageComposerControllerProvider, for a composition you own, such as editing a message in a dialog;
  2. the thread's composer, thread.messageComposer, inside a Thread;
  3. the channel's composer, channel.messageComposer.

Editing uses the same composer. When the user picks "Edit" on a message, the action saves what the composer holds and loads the message into it. The composer is then in edit mode: editedMessage is set in its state, the edited-message preview shows, and sending saves the edit. Cancelling the edit puts back what the user was writing before.

When MessageComposer unmounts, it saves a draft of the thread's or channel's composer, if drafts are turned on, and clears it. A supplied composer is saved but never cleared; it belongs to whoever supplied it. Drafts covers drafts.

Previews

Above the input, the composer shows what the message will carry. Each preview renders only when the composer has something for it:

Preview Shows Override key
Edited message The message being edited, with ✕ to cancel. Takes the quoted message's place while editing. EditedMessagePreview
Quoted message The message being replied to, with ✕ to remove it QuotedMessagePreview
Voice recording A recording waiting to be sent VoiceRecordingPreviewSlot
Attachments Files, images and videos, with their upload progress AttachmentPreviewList
Link previews A card for each link in the text, while it loads and once loaded LinkPreviewList
Location A location to share. It can't be removed while editing. None

The input

TextareaComposer is the text input. It reads and writes textComposer: the text, the cursor position, the mentioned users and the active command.

Typing a trigger opens the suggestion list, SuggestionList: @ suggests people and groups to mention, / suggests commands, and : suggests emoji once you add the emoji middleware from stream-chat-react/emojis to the text composer. Each item is a SuggestionListItem. The suggestions come from the text composer's middleware; Suggestions explains them.

Picking a command, such as /giphy, shows it as a chip before the input, CommandChip. The chip's ✕ clears the command.

Actions

The attachment selector, AttachmentSelector, sits before the input. It opens a menu to upload a file, create a poll, share a location or pick a command. The menu is an action set, explained in Actions.

After the input come two areas. The first, AdditionalMessageComposerActions, shows the emoji picker when you register one as the EmojiPicker override, except during a cooldown. The second shows one control, picked in this order:

  1. The cooldown timer, CooldownTimer, while slow mode stops the user from sending. It shows the seconds left. See Slow mode.
  2. The record button, StartRecordingAudioButton, when the composer is empty, nothing is being edited, no command is active, voice recording is turned on with <MessageComposer audioRecordingEnabled />, and the browser can record. While the user records, AudioRecorder replaces the whole composer. See Voice recordings and audio playback.
  3. The stop button, StopAIGenerationButton, while an AI is thinking or generating a reply in the channel. It calls Channel.stopAIResponse(). Register StopAIGenerationButton: null to turn it off. See SDK integration.
  4. The send button, SendButton, otherwise. It is disabled until the composer has something to send, and shows a checkmark while editing or with a command.

In threads

In a thread, the composer shows a checkbox, SendToChannelCheckbox, that also posts the reply to the channel. It reads "Also send in channel", or "Also send as a direct message" in a channel with two members. It toggles showReplyInChannel in the composer's state, and is hidden while editing.

Drag and drop

Files dropped on the composer are uploaded into it. The composer UI wraps itself in WithDragAndDropUpload, which shows "Drag your files here" while files are dragged over it, and refuses the files that attachments.acceptedFiles in the composer configuration doesn't accept.

To accept drops on a larger area, such as the whole channel, wrap that area in WithDragAndDropUpload. Files dropped anywhere in it go to the composers inside:

src/ChannelWithDropZone.tsx
import {
  Channel,
  ChannelHeader,
  MessageComposer,
  MessageList,
  WithDragAndDropUpload,
} from "stream-chat-react";

export const ChannelWithDropZone = () => (
  <Channel channel={channel}>
    <WithDragAndDropUpload className="channel-drop-zone" component="section">
      <ChannelHeader />
      <MessageList />
      <MessageComposer />
    </WithDragAndDropUpload>
  </Channel>
);

The composer's own drop zone turns off during a cooldown and when the user can't upload files. The props are in the WithDragAndDropUpload reference.

What each part reads

Each part subscribes to the store of the composer piece it shows, and calls that piece when the user acts:

Composer piece Used by
state The quoted and edited message previews, the send button, the also-send-to-channel checkbox
textComposer The input, the suggestion list, the command chip
attachmentManager The attachment and voice recording previews, drag and drop, the attachment selector
linkPreviewsManager The link previews
pollComposer The poll creation dialog
locationComposer The location preview and the share location dialog
customDataManager No built-in part. It holds custom fields that your own parts add to the message.

A part of your own reads the same way: get the composer with useMessageComposerController() and subscribe to a store with useStateStore. This one counts the characters typed, beside the emoji picker:

src/ComposerWithCount.tsx
import type { TextComposerState } from "stream-chat";
import {
  AdditionalMessageComposerActions,
  MessageComposer,
  useMessageComposerController,
  useStateStore,
  WithComponents,
} from "stream-chat-react";

const textSelector = ({ text }: TextComposerState) => ({ text });

const CharacterCount = () => {
  const { textComposer } = useMessageComposerController();
  const { text } = useStateStore(textComposer.state, textSelector);

  return <span className="character-count">{text.length}</span>;
};

// Renders the default part too, so the emoji picker stays.
const ActionsWithCount = () => (
  <>
    <CharacterCount />
    <AdditionalMessageComposerActions />
  </>
);

const overrides = { AdditionalMessageComposerActions: ActionsWithCount };

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

The same part works in a channel, in a thread and while editing, because the hook returns the composer of the place it is rendered in.

Replacing parts

Register replacements with WithComponents. Overriding components explains how overrides apply.

Override key Replaces
MessageComposerUI The whole composer UI. MessageComposer still provides the composer.
EditedMessagePreview, QuotedMessagePreview The edited and quoted message previews
AttachmentPreviewList, VoiceRecordingPreviewSlot The attachment and voice recording previews
LinkPreviewList The link previews
TextareaComposer The input
AutocompleteSuggestionList, AutocompleteSuggestionItem The suggestion list and each suggestion
CommandChip The command chip
AttachmentSelector The attachment selector
AttachmentSelectorInitiationButtonContents The contents of the button that opens the selector
PollCreationDialog, ShareLocationDialog The dialogs the selector opens
AdditionalMessageComposerActions, EmojiPicker The area after the input, and the emoji picker in it
SendButton The send button. Gets sendMessage.
CooldownTimer The cooldown timer
StartRecordingAudioButton, AudioRecorder The record button and the recorder
StopAIGenerationButton The stop button. null turns it off.
SendToChannelCheckbox The also-send-to-channel checkbox

The defaults of EditedMessagePreview, SendToChannelCheckbox and StopAIGenerationButton aren't exported, so a replacement for one of them can't render the default inside it.

The Custom composer recipe builds a composer UI of its own.

Used in