import { useEffect } from "react";
import { setupCommandUIMiddlewares } from "stream-chat-react-native";
const App = () => {
useEffect(() => {
if (!chatClient) {
return;
}
chatClient.setMessageComposerSetupFunction(({ composer }) => {
setupCommandUIMiddlewares(composer);
});
}, [chatClient]);
};Handle Commands UI
To show the command UI in the message input, set up the middlewares in MessageComposer using setupCommandUIMiddlewares:

Best Practices
- Register command middlewares once during client setup to avoid duplicates.
- Keep custom command UI minimal so typing remains fast and predictable.
- Use
useStateStoreselectors to avoid re-renders on unrelated composer state. - Always restore the composer state when closing the command UI.
- Provide a clear escape path (e.g., close button or back gesture).
This adds four middlewares to MessageComposer:
createActiveCommandGuardMiddleware(stream-io/text-composer/active-command-guard) - Prevents new triggers when a command is active.createCommandStringExtractionMiddleware(stream-io/text-composer/command-string-extraction) - Extracts the command string (for example,/ban user→user).createCommandInjectionMiddleware(stream-io/message-composer-middleware/command-string-injection) - This injects the command string into the message composer state.createDraftCommandInjectionMiddleware(stream-io/message-composer-middleware/draft-command-string-injection) - This injects the command string into the draft state.
All of the above middlewares are exported from stream-chat.
The command state in TextComposer is set via createCommandsMiddleware from stream-chat.
Customizing Command UI
To show a custom command UI, pass a custom component via the CommandInput prop on Channel.
import { Button, Text, View } from "react-native";
import {
AutoCompleteInput,
Channel,
useMessageComposer,
useStateStore,
useMessageInputContext,
} from "stream-chat-react-native";
import { TextComposerState } from "stream-chat";
const textComposerStateSelector = (state: TextComposerState) => ({
command: state.command,
});
const CustomInputCommand = () => {
const messageComposer = useMessageComposer();
const { textComposer } = messageComposer;
const { command } = useStateStore(
textComposer.state,
textComposerStateSelector,
);
return (
<View>
<Text style={{ textAlign: "center" }}>{command?.name}</Text>
<AutoCompleteInput />
<Button
onPress={() => {
messageComposer.textComposer.setCommand(null);
messageComposer?.restore();
}}
title="Close"
/>
</View>
);
};
<Channel channel={channel} CommandInput={CustomInputCommand}>
{/* The underlying components */}
</Channel>;The CommandInput component is only rendered when the command state is set in the message composer. Make sure you complete the above middlewares setup to make it work.