Composer UI components
The default input UI is built from the parts on this page. Each part reads the composer from useMessageComposerController(), so it works in a channel, in a thread and while editing. Message composer UI explains what each part shows and which store it reads.
Best practices
- Replace a part by registering it under its override key with
WithComponents, so the default layout keeps the rest. - Render the parts inside
MessageComposer. Several of them, such asTextareaComposerandMessageComposerActions, readMessageComposerContextand throw outside it. - Keep
TextareaComposerin a custom layout, so mentions, commands and the Enter key keep working. - The default layout passes a few parts what they need, such as
commandtoCommandChipandsendMessagetoSendButton. To set any other prop of a part, register a component that renders the part with it.
Available building blocks
| Component | Override key | What it renders | Props |
|---|---|---|---|
TextareaComposer |
TextareaComposer |
The text input and its suggestion list | Below |
CommandChip |
CommandChip |
The selected command, with ✕ to clear it | command. Renders nothing without one |
QuotedMessagePreview |
QuotedMessagePreview |
The message being replied to, with ✕ to remove it | getQuotedMessageAuthor, renderText |
| not exported | EditedMessagePreview |
The message being edited, with ✕ to cancel the edit | message, onCancel |
VoiceRecordingPreviewSlot |
VoiceRecordingPreviewSlot |
A voice recording waiting to be sent | Below |
AttachmentPreviewList |
AttachmentPreviewList |
The files, images and videos in the composer | Below |
LinkPreviewList |
LinkPreviewList |
A card for each link in the text | Below |
AttachmentSelector |
AttachmentSelector |
The button and menu to upload a file, create a poll, share a location or pick a command | Attachment Selector |
AdditionalMessageComposerActions |
AdditionalMessageComposerActions |
The area after the input. It renders the EmojiPicker override, except during a cooldown |
none |
EmojiPicker, from stream-chat-react/emojis |
EmojiPicker |
The emoji picker button and popover | Emoji Picker |
MessageComposerActions |
none | One of: the cooldown timer, the record button, the stop button for an AI reply, the send button | none |
SendButton |
SendButton |
The send button. Disabled until the composer has something to send | sendMessage, and any <button> prop |
CooldownTimer |
CooldownTimer |
The seconds left of a slow-mode cooldown | none |
StartRecordingAudioButton |
StartRecordingAudioButton |
The record button | Audio Recorder |
AudioRecorder |
AudioRecorder |
The recorder, in place of the composer while recording | Audio Recorder |
| not exported | StopAIGenerationButton |
The button that stops an AI reply. null turns it off |
any <button> prop |
| not exported | SendToChannelCheckbox |
The "Also send in channel" checkbox, in a thread and not while editing | none |
WithDragAndDropUpload |
none | A drop zone that uploads files into the composers inside it | WithDragAndDropUpload |
FileDragAndDropContent |
none | The "Drag your files here" overlay of WithDragAndDropUpload |
isDragRejected |
WithComponents accepts a FileDragAndDropContent key, but WithDragAndDropUpload doesn't read it and always renders the default overlay.
MessageComposerActions picks its control in this order: the cooldown timer during a cooldown; the record button when the composer is empty, nothing is being edited, no command is active and recording is turned on; the stop button while an AI reply is being generated; the send button otherwise. While editing or with a command, the default send button shows a checkmark.
Command-state behavior
When the user selects a command, such as /giphy, the default layout:
- adds the
str-chat__message-composer--command-activeclass to the composer; - shows
CommandChipbefore the input; - hides
AttachmentSelectorandAdditionalMessageComposerActionswith a CSS transition, and takes them out of the tab order; - leaves the record button out of
MessageComposerActions; - changes the placeholder of the input to the command's arguments, or to "Search GIFs" for
/giphy.
Escape, or Backspace in an empty input, clears the command. A custom layout gets the chip and the placeholder from the parts, and decides the rest itself. Commands and Giphy explains commands.
TextareaComposer props
TextareaComposer takes every <textarea> prop except defaultValue, disabled, style and value, and these:
| Prop | Type | Default | Description |
|---|---|---|---|
closeSuggestionsOnClickOutside |
boolean |
true |
Closes the suggestion list when the user clicks outside it. |
containerClassName |
string |
none | Class name added to the div around the textarea and the suggestion list. |
listClassName |
string |
none | Class name added to the suggestion list. |
maxRows |
number |
the MessageComposer prop, or 10 |
The most rows the input grows to. |
minRows |
number |
the MessageComposer prop |
The fewest rows the input shows. Without it, an empty input is one row. |
shouldSubmit |
(event: React.KeyboardEvent<HTMLTextAreaElement>) => boolean |
the MessageComposer prop, or Enter without Shift |
Decides which key press submits. |
Some <textarea> props behave in a particular way:
placeholderreplaces "Send a message". A command's placeholder and the cooldown's "Slow mode, wait 5s..." still take its place.classNameis added to the default class names.onChange,onKeyDownandonScrollreplace the default handling. With your ownonChange, typing no longer reaches the composer. With your ownonKeyDown, Enter no longer submits and the arrow keys no longer move through suggestions.onSelectruns before the default handling, which keeps the composer's cursor position.onPasteis ignored. The input always uses the paste handler fromMessageComposerContext.
The input is disabled while the composer's text.enabled setting is false, and during a slow-mode cooldown.
LinkPreviewList props
| Prop | Type | Default | Description |
|---|---|---|---|
displayLinkCount |
number |
1 |
How many link previews show, in text order. |
The list shows the previews that are loading or loaded. To show more than one, register a LinkPreviewList that renders the default with displayLinkCount. Link previews explains them.
AttachmentPreviewList slots
AttachmentPreviewList renders the files, images and videos in the composer, each with its upload progress, and opens images and videos in a gallery. It leaves out:
- link previews, which
LinkPreviewListrenders; - voice recordings, which
VoiceRecordingPreviewSlotrenders; - the location to share, which the default layout renders separately.
Each slot replaces the preview of one kind of attachment:
| Prop | Type | Default | Previews |
|---|---|---|---|
AudioAttachmentPreview |
ComponentType<AudioAttachmentPreviewProps> | ComponentType<FileAttachmentPreviewProps> |
the file preview | Audio files |
FileAttachmentPreview |
ComponentType<FileAttachmentPreviewProps> |
the file preview | Other files |
ImageAttachmentPreview |
ComponentType<ImageAttachmentPreviewProps> |
the media preview | Images |
UnsupportedAttachmentPreview |
ComponentType<UnsupportedAttachmentPreviewProps> |
UnsupportedAttachmentPreview |
Attachments of any other type |
VideoAttachmentPreview |
ComponentType<MediaAttachmentPreviewProps> |
the media preview | Videos |
Each preview gets the attachment, handleRetry to upload it again and removeAttachments to remove it. Image and video previews also get openPreview, which opens the gallery at that attachment. The default file and media previews aren't exported. AudioAttachmentPreviewProps and MediaAttachmentPreviewProps aren't exported either; read them from AttachmentPreviewListProps, as in AttachmentPreviewListProps["VideoAttachmentPreview"].
To set the slots, register an AttachmentPreviewList that renders the default with them. The Attachment previews recipe does this.
Upload progress and size indicators
The previews show an upload with these components:
| Component | Props | What it renders |
|---|---|---|
UploadProgressIndicator |
uploadProgress, uploadConfirmationPending |
The ProgressIndicator override, a ring showing the percent sent. The LoadingIndicator override when the progress is unknown, or when every byte is sent and the server hasn't confirmed yet. |
AttachmentUploadedSizeIndicator |
attachment, uploadProgress |
While uploading, the UploadedSizeIndicator override with the bytes sent and the total. Once uploaded, the FileSizeIndicator override with the file's size. |
UploadedSizeIndicator |
uploadedBytes, fullBytes |
"sent / total", each formatted by the FileSizeIndicator override. |
FileSizeIndicator |
fileSize, maximumFractionDigits |
A byte size, such as "2.4 MB". |
By default:
- file previews show the progress ring and the size while uploading, and the size once uploaded;
- voice recording previews show the progress ring while uploading, and the size until the recording's duration is known;
- image and video previews show the progress ring over the thumbnail, and no size.
To change these without replacing the previews, register ProgressIndicator, LoadingIndicator, UploadedSizeIndicator or FileSizeIndicator with WithComponents. ProgressIndicator gets percent, from 0 to 100.
VoiceRecordingPreviewSlot props
| Prop | Type | Default | Description |
|---|---|---|---|
VoiceRecordingPreview |
ComponentType<VoiceRecordingPreviewProps> |
the voice recording player | The preview of the recording, which the user can play before sending. |
The slot shows the first voice recording in the composer, above the attachment list. It gets the same props as the attachment previews: attachment, handleRetry and removeAttachments. Voice recordings and audio playback explains recording.
Hooks
useAttachmentManagerState: the attachments and how many more fit.useAttachmentsForPreview: the attachments, the poll and the location to preview.useMessageComposerHasSendableData: whether the send button is enabled.useMessageComposerCommands: the channel's commands and whether each one can be used.