This is beta documentation for Stream Chat React SDK v14. For the latest stable version, see the latest version (v13) .

Message Actions

This example shows how to add custom actions to the SDK message action menu.

Best Practices

  • Start from defaultMessageActionSet unless you need a fully custom action surface.
  • Keep custom actions small and read message state from hooks like useMessageContext().
  • Register custom actions with WithComponents so the same override works in MessageList, Thread, and VirtualizedMessageList.
  • Keep action handlers fast and idempotent.
  • Re-check custom actions in both channel and thread views because the SDK filters some actions differently across contexts.

Built-in Actions

The default action set can include these action types:

  • delete
  • edit
  • flag
  • markUnread
  • mute
  • pin
  • quote
  • react
  • remindMe
  • reply
  • resend
  • saveForLater

Add A Custom Dropdown Action

Use defaultMessageActionSet as a base and append your own dropdown item.

import {
  type ContextMenuItemProps,
  Channel,
  MessageActions,
  WithComponents,
  defaultMessageActionSet,
  useMessageContext,
} from "stream-chat-react";

const YellAction = ({ closeMenu }: ContextMenuItemProps) => {
  const { message } = useMessageContext("YellAction");

  return (
    <button
      className="str-chat__message-actions-list-item-button"
      onClick={() => {
        window.alert(`Yell action clicked on message: ${message.id}!`);
        closeMenu();
      }}
    >
      Yell
    </button>
  );
};

const CustomMessageActions = () => (
  <MessageActions
    messageActionSet={[
      ...defaultMessageActionSet,
      {
        Component: YellAction,
        placement: "dropdown",
        type: "yell",
      },
    ]}
  />
);

export const WrappedChannel = ({ children }) => (
  <WithComponents overrides={{ MessageActions: CustomMessageActions }}>
    <Channel>{children}</Channel>
  </WithComponents>
);

Add A Quick Action

Quick actions render inline next to the message instead of inside the dropdown menu.

import {
  Channel,
  MessageActions,
  WithComponents,
  defaultMessageActionSet,
  useMessageContext,
} from "stream-chat-react";

const CopyIdAction = () => {
  const { message } = useMessageContext("CopyIdAction");

  return (
    <button onClick={() => navigator.clipboard.writeText(message.id)}>
      Copy ID
    </button>
  );
};

const CustomMessageActions = () => (
  <MessageActions
    messageActionSet={[
      ...defaultMessageActionSet,
      {
        Component: CopyIdAction,
        placement: "quick",
        type: "copy-id",
      },
    ]}
  />
);

export const WrappedChannel = ({ children }) => (
  <WithComponents overrides={{ MessageActions: CustomMessageActions }}>
    <Channel>{children}</Channel>
  </WithComponents>
);

Filter The Defaults

The SDK filters the default action set based on permissions, message state, and thread context. In most cases, keep that filtering enabled and only remove or add items intentionally.

import { MessageActions, defaultMessageActionSet } from "stream-chat-react";

const CustomMessageActions = () => (
  <MessageActions
    messageActionSet={defaultMessageActionSet.filter(
      ({ type }) => type !== "markUnread",
    )}
  />
);

If you pass disableBaseMessageActionSetFilter, the SDK stops applying that default permission/state filtering. Only do that if your custom actions already enforce the same constraints.