Custom Message Actions

Message actions appear in the message menu when you long-press a message. You can control them granularly. By default, the following actions are shown:

Message Actions

Best Practices

  • Start from default actions and selectively remove or reorder to preserve platform expectations.
  • Keep action lists short and context-aware (own message vs. others, threads vs. channel).
  • Prefer messageActions for UI changes and onLongPressMessage for custom behavior.
  • Ensure destructive actions (delete, ban, mute) are gated by capabilities and confirmations.
  • Return null for actions you explicitly want to hide instead of leaving them undefined.

Each UI action maps to a MessageAction object that provides title, icon, handler, and actionType.

type MessageActionType = {
  action: () => void;
  actionType: enum('blockUser', 'copyMessage', 'deleteMessage', 'deleteForMeMessage', 'editMessage', 'flagMessage', 'markUnread', 'muteUser', 'pinMessage', 'selectReaction', 'reply', 'retry', 'quotedReply', 'threadReply', 'unpinMessage');
  title: string;
  icon?: React.ReactElement;
  titleStyle?: StyleProp<TextStyle>;
};

You can customize default actions via props on the Channel component. These props are available through MessagesContext.

Channel accepts a messageActions callback to selectively render actions.

The callback receives an object with default MessageAction objects. Return an array of actions to render in MessageActionList inside the message overlay.

Example:

messageActions={({
  blockUser, // MessageAction | null;
  copyMessage, // MessageAction | null;
  deleteMessage, // MessageAction | null;
  dismissOverlay, // () => void;
  editMessage, // MessageAction | null;
  error, // boolean;
  flagMessage, // MessageAction | null;
  isThreadMessage, // boolean;
  markUnread, // MessageAction | null;
  muteUser, // MessageAction | null;
  ownCapabilities, // object;
  pinMessage, // MessageAction | null;
  quotedReply, // MessageAction | null;
  retry, // MessageAction | null;
  showMessageReactions, // boolean;
  threadReply, // MessageAction | null;
  unpinMessage, // MessageAction | null;
}) => {
  return []; // Array<MessageAction>
}}

Message Action Object type

MessageAction

Long-pressing a message opens the menu and renders available actions. MessageAction defines a single action button.

Example

{
  action: () => { /** Some action here */ },
  icon: <PinIcon />,
  title: 'Pin Message',
  titleStyle: { color: 'red' }
}

Keys and Values

action

Called when the user presses the action.

Type
Function

actionType

Type of the action performed.

Type
enum('blockUser', 'copyMessage', 'deleteMessage', 'deleteMessageForMe', 'editMessage', 'flagMessage', 'markUnread', 'muteUser', 'pinMessage', 'selectReaction', 'reply', 'retry', 'quotedReply', 'threadReply', 'unpinMessage')

icon

Icon element for the action button.

Type
React Element

title

Action button title.

Type
String

titleStyle

Styles for the action title text.

MessagePressableHandlerPayload

MessagePressableHandlerPayload is passed to handlers like onLongPressMessage and onPressMessage.

Example

{
  actionHandlers: {
    deleteMessage: [function],
    editMessage: [function],
    quotedReply: [function],
    resendMessage: [function],
    showMessageOverlay: [function],
    toggleBanUser: [function],
    toggleMuteUser: [function],
    toggleReaction: [function],
  },
  additionalInfo: [object],
  message: [message object],
}

Keys and values

additionalInfo

Additional message pressable handler info.

Type
Object

actionHandlers

Action handler functions for message actions. Use these to perform actions on interaction.

  • deleteMessage
  • editMessage
  • quotedReply
  • resendMessage
  • showMessageOverlay
  • toggleBanUser
  • toggleMuteUser
  • toggleReaction (takes reactionType as parameter)
<Channel onLongPressMessage={({ defaultHandlers }) => defaultHandlers.quotedReply()}
Type
Object

message

Message object that was interacted with.

<Channel onLongPressMessage={({ message }) => console.log(message.id, message.text)}
Type
Object

How to conditionally render message actions

The following example demonstrates how to:

  • Only show the "Copy Message" and "Edit Message" actions from the default message actions.
  • Show "Edit Message" only for messages from the current user.

Additionally, the following example demonstrates how you can add custom styles for a message action title.

<Channel
  messageActions={({ copyMessage, deleteMessage, editMessage, isMyMessage }) =>
    isMyMessage
      ? [
          copyMessage,
          editMessage,
          {
            ...deleteMessage,
            titleStyle: {
              color: "red",
              fontWeight: "bold",
            },
          },
        ]
      : [copyMessage]
  }
>
  {/** MessageList and MessageInput component here */}
</Channel>

How to add a custom message action

  • Add a new custom action - "Mute User"
  • Show "Mute User" action only for messages from other user.
import {
  messageActions as defaultMessageActions,
  Mute as MuteIcon,
} from "stream-chat-react-native";

<Channel
  channel={channel}
  messageActions={(param) => {
    const { isMyMessage, ownCapabilities, dismissOverlay } = param;
    const actions = defaultMessageActions({ ...param });

    if (!isMyMessage) {
      const isMuted = (client.mutedUsers || []).some(
        (mute) =>
          mute.user.id === client.userID && mute.target.id === message.user?.id,
      );

      actions.push({
        action: async () => {
          dismissOverlay();
          if (message.user?.id) {
            if (isMuted) {
              await client.unmuteUser(message.user.id);
            } else {
              await client.muteUser(message.user.id);
            }
          }
        },
        actionType: "custom-mute-user",
        icon: <MuteIcon />,
        title: isMuted ? t("Custom Unmute User") : t("Custom Mute User"),
      });
    }

    return actions;
  }}
>
  {/** MessageList and MessageInput component here */}
</Channel>;

How to customize message action UI

Channel accepts MessageActionList and MessageActionListItem. They serve different purposes:

  • MessageActionList - Full control over the action list and its styles.
  • MessageActionListItem - Customize a single action item.

Customize a Message Action List

Example MessageActionList:

import { Alert } from "react-native";
import {
  Channel,
  MessageActionListItem,
  useMessageContext,
} from "stream-chat-react-native";

const CustomMessageActionList = () => {
  const { dismissOverlay } = useMessageContext();
  const messageActions = [
    {
      action: () => {
        Alert.alert("Edit Message action called.");
        dismissOverlay();
      },
      actionType: "editMessage",
      title: "Edit message",
    },
    {
      action: () => {
        Alert.alert("Delete message action");
        dismissOverlay();
      },
      actionType: "deleteMessage",
      title: "Delete Message",
    },
  ];
  return (
    <View style={{ backgroundColor: "white" }}>
      {messageActions.map(({ actionType, ...rest }) => (
        <MessageActionListItem
          actionType={actionType}
          key={actionType}
          {...rest}
        />
      ))}
    </View>
  );
};

<Channel MessageActionList={CustomMessageActionList}>
  {/* Underlying MessageList and MessageInput components */}
</Channel>;

MessageActionList is just a prop that accepts your component. You define content, styles, and logic.

Customize Message Action list item

To customize pinMessage and muteUser, check actionType and return your own component.

The types of actions which are available by default are as follows:

  • blockUser
  • copyMessage
  • deleteMessage
  • deleteForMeMessage
  • editMessage
  • flagMessage
  • markUnread
  • muteUser
  • pinMessage
  • selectReaction
  • reply
  • retry
  • quotedReply
  • threadReply
  • unpinMessage

If no custom condition matches, return the default MessageActionListItem.

You can use these props to provide your own component.

Example MessageActionListItem customization:

import { Pressable, Text } from "react-native";
import {
  Channel,
  MessageActionListItem,
  useMessageActionAnimation,
} from "stream-chat-react-native";

const CustomMessageActionListItem = ({
  action,
  actionType,
  ...rest
}: MessageActionListItemProps) => {
  if (actionType === "pinMessage") {
    return (
      <Pressable onPress={action}>
        <Text>{actionType}</Text>
      </Pressable>
    );
  } else if (actionType === "muteUser") {
    return (
      <Pressable onPress={action}>
        <Text>{actionType}</Text>
      </Pressable>
    );
  } else {
    return (
      <MessageActionListItem
        action={action}
        actionType={actionType}
        {...rest}
      />
    );
  }
};

<Channel MessageActionListItem={CustomMessageActionListItem}>
  {/* Underlying MessageList and MessageInput components */}
</Channel>;

Please continue reading further to see different use cases.

How to intercept a message action

To add analytics tracking, use these handler props (called before the default handlers):

Please note that these intercepts will neither change the standard functions nor block them.

  • handleBan
  • handleCopy
  • handleDelete
  • handleDeleteForMe
  • handleEdit
  • handleFlag
  • handleMute
  • handleReaction
  • handleReply
  • handleRetry
  • handleThreadReply

Example: track "Copy Message":

<Channel handleCopy={() => trackCopyAction()} />

How to disable a message action

To disable an action, return null for that actionType in MessageActionListItem:

import {
  Channel,
  MessageActionListItem,
  useMessageActionAnimation,
} from "stream-chat-react-native";

const CustomMessageActionListItem = ({ action, actionType, ...rest }) => {
  if (actionType === "pinMessage") {
    return null;
  } else {
    return (
      <MessageActionListItem
        action={action}
        actionType={actionType}
        {...rest}
      />
    );
  }
};

<Channel MessageActionListItem={CustomMessageActionListItem}>
  {/* Underlying MessageList and MessageInput components */}
</Channel>;