new MessageComposer({
client, // StreamChat client instance
compositionContext, // it is necessary to provide a Channel or Thread instance or LocalMessage object
composition, // optional initial state like draft message or edited message
config, // optional custom configuration
});
MessageComposer Class
MessageComposer Class
The MessageComposer
serves as a central orchestrator for message creation and editing. It:
- Manages the state of message composition
- Handles different types of content (text, attachments, polls, etc.)
- Provides draft functionality for saving message progress
- Supports message editing and quoting
- Integrates with the Stream Chat API for message operations
We can initiate the composer instance as follows:
Each Channel
and Thread
instance has its own messageComposer
attribute assigned on instantiation. In case we need to edit a message, we pass the LocalMessage
object to compositionContext
and composition
constructor parameters.
MessageComposer
is built using a modular architecture with specialized managers for different aspects of message composition:
- TextComposer: Handles text input, mentions, and text-related operations
- AttachmentManager: Keeps message attachments state and manages file attachments uploads
- LinkPreviewsManager: Processes and manages link previews in messages
- PollComposer: Handles poll composition and creation
- CustomDataManager: Allows for custom message and non-message related data
Each manager operates independently and keeps its own dedicated state. MessageComposer
orchestrates their interactions.
The following are the general MessageComposer
features:
- State Management: Uses a reactive state management system to track changes
- Middleware Support: Allows for custom processing of messages through middleware
- Draft System: Supports saving and restoring message drafts
- Rich Content: Handles various types of content including text, attachments, polls, and link previews
- Event System: Provides event subscriptions for real-time updates
- Configurable: Highly configurable through the MessageComposerConfig interface
StreamChat.setMessageComposerSetupFunction
One MessageComposer
setup function per client, ideally you’d setup this function only once (or each time a specific dependency changes).
import { defaultTextComposerMiddlewares } from "stream-chat";
const chatClient = useCreateChatClient({
apiKey,
tokenOrProvider: userToken,
userData: { id: userId, language: "en" },
});
const [emojisEnabled, setEmojisEnabled] = useState(false);
useEffect(() => {
chatClient.setMessageComposerSetupFunction(({ composer }) => {
if (composer.contextType === "channel") {
composer.textComposer.upsertMiddleware(
defaultTextComposerMiddlewares.map(composer.channel),
);
if (emojisEnabled) {
composer.textComposer.upsertMiddleware([
createEmojiMiddleware(SearchIndex),
]);
}
return () => {
composer.textComposer.removeMiddleware("emoji");
};
}
});
}, [chatClient, emojisEnabled]);
Message Composer Configuration
Configuration
The MessageComposer
can be configured at different levels, from global client-wide settings to individual manager configurations.
Configuration Structure
The configuration is defined by the MessageComposerConfig
interface:
type MessageComposerConfig = {
drafts: {
enabled: boolean; // Enables server-side draft functionality. Disabled by default.
};
attachments: {
/**
* Function to filter out files before being uploaded. The default forwards without filtering.
*/
fileUploadFilter: (file: FileLike) => boolean;
/**
* Maximum file upload attachments per message. Default is 10.
*/
maxNumberOfFilesPerMessage: number;
/**
* Allowed file types.
* Expected are unique file type specifiers
* (https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept#unique_file_type_specifiers).
* The default is an empty array - all files accepted.
*/
acceptedFiles: string[];
/**
* Custom upload request (and possibly logic). For example upload to custom CDN.
*/
doUploadRequest?: (
file: FileLike,
) => Promise<{ file: string; thumb_url?: string }>;
};
linkPreviews: {
/**
* Enables link previews. Disabled by default.
*/
enabled: boolean;
/**
* Debounce time for URL detection and enrichment. By default 1500ms.
*/
debounceURLEnrichmentMs: number;
/**
* Custom URL detection function. The default is linkifyjs.find.
*/
findURLFn: (text: string) => string[];
/**
* Custom logic to be invoked when a specific link preview is dismissed
*/
onLinkPreviewDismissed?: (linkPreview: LinkPreview) => void;
};
text: {
/**
* Enables text input. Enabled by default.
*/
enabled: boolean;
/**
* Enable sending typing events which are used to display typing indicators. Enabled by default.
*/
publishTypingEvents: boolean;
/**
* Initial text value if draft or existing message is not available.
*/
defaultValue?: string;
/**
* The message text will be trimmed to maxLengthOnEdit on text change or suggestion selection.
*/
maxLengthOnEdit?: number;
/**
* The message text will be trimmed to maxLengthOnSend during the final message composition before being sent.
*/
maxLengthOnSend?: number;
};
};
Configuration Methods
1. Global Configuration
Use client.setMessageComposerSetupFunction
to configure all MessageComposer
instances:
client.setMessageComposerSetupFunction(({ composer }) => {
composer.updateConfig({
drafts: { enabled: true },
attachments: {
maxNumberOfFilesPerMessage: 5,
acceptedFiles: ["image/*", "video/*"],
},
});
});
This configuration will be applied also to all new MessageComposer
instances (for example created for editing a message).
2. Instance-Specific Configuration
Configure a specific MessageComposer
instance using updateConfig
:
messageComposer.updateConfig({
text: {
enabled: true,
publishTypingEvents: false,
},
});
3. Manager-Level Configuration
Each manager can be configured individually through its properties:
// Configure TextComposer
messageComposer.textComposer.defaultValue = "Dear Customer Service";
// Configure AttachmentManager
messageComposer.attachmentManager.acceptedFiles(["image/*", "video/*"]);
// Configure LinkPreviewsManager
messageComposer.linkPreviewsManager.enabled = true;
// General message composition parameters. Applies to drafts.
messageComposer.updateConfig({ drafts: enabled });
Configuring the message id generation
We can provide a custom message id generator function by overriding the MessageComposer.generateId
static method:
messageComposer.generateId = customGenerator;
Message Composer Context Type
Each MessageComposer
has a contextType
getter which gets evaluated from the compositionContext
value provided at construction time. A composer can have a context type of channel
, thread
, legacy_thread
or message
.
You might not need this information but it’s there when you need it; for example when you’re deciding what middlewares to apply to only channel-based or thread-based composers.
:::note
Type legacy_thread
is a simple message object extended with legacyThreadId
property whose value equals to that message’s id
.
:::
Accessing the Corresponding MessageComposer instance
The useMessageComposer
hook provides access to the appropriate MessageComposer
instance based on the current context (channel, thread, message). It automatically handles different composition scenarios:
- Composing a new message in a channel
- Replying in a thread
- Editing an existing message
- Replying in a legacy thread that does not rely on Thread instance
The usage:
import { useMessageComposer } from "stream-chat-react";
const MyComponent = () => {
const messageComposer = useMessageComposer();
const handleSend = async () => {
// Access composer features
const { localMessage, message, sendOptions } =
await messageComposer.compose();
// Handle the composed message
// ...
};
return <button onClick={handleSend}>Send</button>;
};
The hook automatically resolves the correct MessageComposer
instance based on the following hierarchy:
- Editing a message: If a message is being edited, a new composer instance is created for that specific message
- Thread reply: If in a thread context, it uses the thread’s composer instance
- Legacy thread reply: If replying to a message in a context that does not rely on
Thread
instance that already contains itsMessageComposer
instance. - Channel message: Falls back to the channel’s composer instance
It automatically does the following:
- Registers the necessary subscriptions when the composer is mounted
- Cleans up subscriptions when the component unmounts
The hook implements caching for composer instances in certain contexts:
- Editing a message
- Replying in a legacy thread
This ensures that the same composer instance is reused when returning to these contexts, preserving the composition state.
Message Composer Utility Hooks
useAttachmentManagerState
const {
attachments,
availableUploadSlots,
blockedUploadsCount,
failedUploadsCount,
isUploadEnabled,
pendingUploadsCount,
successfulUploadsCount,
uploadsInProgressCount,
} = useAttachmentManagerState();
Use this hook when you need to:
- Access the attachment data
- Track attachment upload progress and status
- Show upload progress indicators
- Enable/disable UI elements based on upload state
- Display attachment counts and limits
useCanCreatePoll
const canCreatePoll = useCanCreatePoll();
Use this hook to:
- Determine if a poll can be created with the current state
- Enable/disable the send button
useMessageComposerHasSendableData
const hasSendableData = useMessageComposerHasSendableData();
Use this hook to:
- Enable/disable the send button