import { MessageActions } from "stream-chat-react";Message Actions
MessageActions renders the actions available for a message. Even though this page still lives under experimental-features in the docs tree, the component is part of the main stream-chat-react package:
Best Practices
- Start from
defaultMessageActionSetunless you need a fully custom action surface. - Keep custom action components small and read message state from hooks like
useMessageContext(). - Use
WithComponentsto scope action overrides to the part of the app that needs them. - Define a custom
messageActionSetexplicitly if your app depends on a stable action order. - Re-check custom actions in both channel and thread message lists, because the SDK filters some actions differently across those contexts.
How MessageActions Works
An action-set item has:
- a
type - a
placementof eitherquickordropdown - a
Component
Quick actions render inline next to the message. Dropdown actions render inside the ContextMenu opened by the actions button.
The exported defaultMessageActionSet already includes the default SDK actions and ordering. In most cases, start from that set and filter or extend it.
Basic Customization
This example removes the default delete action and adds one custom dropdown action.
import {
type ContextMenuItemProps,
Channel,
ChannelHeader,
MessageActions,
MessageInput,
MessageList,
Thread,
Window,
WithComponents,
defaultMessageActionSet,
useMessageContext,
} from "stream-chat-react";
const BookmarkAction = ({ closeMenu }: ContextMenuItemProps) => {
const { message } = useMessageContext();
return (
<button
onClick={() => {
saveMessage(message.id);
closeMenu();
}}
>
Save for later
</button>
);
};
const CustomMessageActions = () => {
const messageActionSet = [
...defaultMessageActionSet.filter(({ type }) => type !== "delete"),
{
Component: BookmarkAction,
placement: "dropdown" as const,
type: "bookmark",
},
];
return <MessageActions messageActionSet={messageActionSet} />;
};
const App = () => (
<WithComponents overrides={{ MessageActions: CustomMessageActions }}>
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageInput />
</Window>
<Thread />
</Channel>
</WithComponents>
);Quick Actions
Quick actions are regular React components with no menu props. They usually read all needed state from context hooks.
import {
Channel,
MessageActions,
WithComponents,
defaultMessageActionSet,
useMessageContext,
} from "stream-chat-react";
const CopyIdAction = () => {
const { message } = useMessageContext();
return (
<button onClick={() => navigator.clipboard.writeText(message.id)}>
Copy ID
</button>
);
};
const CustomMessageActions = () => (
<MessageActions
messageActionSet={[
...defaultMessageActionSet,
{
Component: CopyIdAction,
placement: "quick",
type: "copy-id",
},
]}
/>
);
const App = () => (
<WithComponents overrides={{ MessageActions: CustomMessageActions }}>
<Channel>{/* ... */}</Channel>
</WithComponents>
);Filtering And Permissions
MessageActions applies the SDK base filter by default. That filter removes actions that do not make sense for the current message, user permissions, thread state, or channel capabilities.
Use the default filter unless you are intentionally taking over permission logic yourself.
import { MessageActions, defaultMessageActionSet } from "stream-chat-react";
const CustomMessageActions = () => {
const messageActionSet = defaultMessageActionSet.filter(
({ type }) => type !== "markUnread",
);
return <MessageActions messageActionSet={messageActionSet} />;
};If you pass disableBaseMessageActionSetFilter, your custom set is used as-is. Only do that when your custom action logic already enforces the same constraints the SDK would normally enforce.