# Actions

> **Info:** This is release candidate documentation for **Stream Chat React Native SDK v10**. For the latest stable version, see the [latest version (v9)](https://getstream.io/chat/docs/sdk/react-native/).

Every menu of actions in the SDK is built from a list of action items: each one has a label, an icon, a handler, and an identifier. To change a menu, you give the component that builds it a function that receives the default items and returns the list to render. There are three such lists:

| Actions                             | Where they show                                                       | Changed through                                                                 | Default list                           |
| ----------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -------------------------------------- |
| [Message actions](#message-actions) | the message menu, opened by a long press on a message                 | `messageActions` on `Channel` and `Thread`                                      | `messageActions`                       |
| [Channel actions](#channel-actions) | a channel-list row's swipe actions and options sheet, channel details | `getChannelActionItems` on `ChannelList`, and on `ChannelDetailsActionsSection` | `buildDefaultChannelActionItems`       |
| [Member actions](#member-actions)   | the sheet that opens on a member in channel details                   | `getChannelMemberActionItems` on `ChannelMemberActionsSheet`                    | `buildDefaultChannelMemberActionItems` |

Each list also has hooks that run the same actions from components of your own. Everything on this page is exported from `stream-chat-react-native`.

## Message actions

A long press on a message opens its menu: the reaction picker above the message, and the action list below it. The action list is built when the menu opens, not when the message renders, so a long list or an expensive check costs nothing until the user asks for it.

`Channel` and `Thread` take a `messageActions` function. When a menu opens, the SDK builds every default action for the message, passes them to the function with what it knows about the message, and renders the list it returns, in order. Without the prop, the SDK uses its own `messageActions` function, which is exported, so yours can start from its result:

```tsx
import { useCallback } from "react";
import { Share } from "react-native";
import type { Channel as StreamChannel } from "stream-chat";
import {
  asDynamicKey,
  Channel,
  MessageComposer,
  MessageList,
  messageActions as defaultMessageActions,
  useComponentsContext,
  useTheme,
  useTranslationContext,
} from "stream-chat-react-native";
import type {
  MessageActionsProp,
  MessageActionType,
} from "stream-chat-react-native";

export const ChannelScreen = ({ channel }: { channel: StreamChannel }) => {
  const { icons } = useComponentsContext();
  const { t } = useTranslationContext();
  const {
    theme: { semantics },
  } = useTheme();

  const messageActions = useCallback<MessageActionsProp>(
    (params) => {
      // The SDK's list for this message, without "Block User".
      const actions = defaultMessageActions(params).filter(
        (action) => action.actionType !== "blockUser",
      );

      const text = params.message.text;
      if (!text || params.error) return actions;

      const share: MessageActionType = {
        action: () => {
          void Share.share({ message: text });
        },
        actionType: "shareMessage",
        icon: (
          <icons.Share
            height={20}
            stroke={semantics.textSecondary}
            width={20}
          />
        ),
        title: t(asDynamicKey("myApp.message.share.label"), "Share"),
        type: "standard",
      };

      return [share, ...actions];
    },
    [icons, semantics.textSecondary, t],
  );

  return (
    <Channel channel={channel} messageActions={messageActions}>
      <MessageList />
      <MessageComposer />
    </Channel>
  );
};
```

1. The user long-presses a message, and the menu opens.
2. The SDK calls `messageActions` with the message's actions and context.
3. `defaultMessageActions(params)` returns the list the SDK would show: the actions that apply to this message and to the user's capabilities, in the SDK's order.
4. The function drops "Block User", and puts "Share" first when the message has text.
5. Pressing an item closes the menu, and runs the item's `action` once the menu has closed.

`Thread` doesn't inherit props from `Channel`, so pass the same `messageActions` to both.

### What the function receives

| Field                                                                                                                                                                                                     | What it is                                                                                      |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `banUser`, `blockUser`, `copyMessage`, `deleteForMeMessage`, `deleteMessage`, `editMessage`, `flagMessage`, `markUnread`, `muteUser`, `pinMessage`, `quotedReply`, `retry`, `threadReply`, `unpinMessage` | one `MessageActionType` for each default action, whether or not it applies to this message      |
| `message`                                                                                                                                                                                                 | the message the menu is open on                                                                 |
| `isMyMessage`                                                                                                                                                                                             | whether the current user sent it                                                                |
| `isThreadMessage`                                                                                                                                                                                         | whether the menu is inside a `Thread`, or the message is a reply                                |
| `error`                                                                                                                                                                                                   | whether the message is an error or failed to send                                               |
| `ownCapabilities`                                                                                                                                                                                         | the user's capabilities in the channel, as booleans such as `pinMessage` and `deleteAnyMessage` |
| `dismissOverlay`                                                                                                                                                                                          | closes the menu                                                                                 |
| `updateMessage`                                                                                                                                                                                           | updates the message in the message list                                                         |

The action objects are built for every message, so a function that returns them directly, such as `({ copyMessage, editMessage }) => [copyMessage, editMessage]`, shows them even where they don't apply: an edit action on someone else's message, a copy action on a message without text. Check the fields yourself, or filter the result of `defaultMessageActions(params)` as the example does.

### What the default list holds

The SDK's `messageActions` adds an action when its condition holds, in this order:

| Action                       | Shown when                                                                                                      |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `retry`                      | the message is the user's own and failed to send                                                                |
| `threadReply`                | the user has `sendReply`, outside a thread, and the message didn't fail                                         |
| `quotedReply`                | the user has `quoteMessage`, outside a thread, and the message didn't fail                                      |
| `pinMessage`, `unpinMessage` | the user has `pinMessage`; one or the other, depending on whether the message is pinned                         |
| `editMessage`                | the user may update this message (`updateOwnMessage` or `updateAnyMessage`), and it has no Giphy, Imgur or poll |
| `copyMessage`                | a clipboard handler is available, the message has text, and it didn't fail                                      |
| `markUnread`                 | the user has `readEvents`, the message is someone else's, outside a thread, and it didn't fail                  |
| `banUser`                    | the message is someone else's and the user has `banChannelMembers`                                              |
| `deleteMessage`              | the user may delete this message (`deleteOwnMessage` or `deleteAnyMessage`)                                     |
| `flagMessage`                | the message is someone else's and the user has `flagMessage`                                                    |
| `muteUser`, `blockUser`      | the message is someone else's                                                                                   |

On a failed message, only `retry`, `editMessage` and `deleteMessage` stay. `deleteForMeMessage`, which deletes the message for the current user only, is never in the default list: add it in your function to offer it.

The menu renders the `standard` actions first and the `destructive` ones after a separator, each group in the order of your list. `banUser`, `blockUser`, `deleteMessage` and `deleteForMeMessage` are destructive.

### MessageActionType

| Field        | Type                          | Description                                                          |
| ------------ | ----------------------------- | -------------------------------------------------------------------- |
| `action`     | `() => void`                  | Runs when the item is pressed, after the menu has closed.            |
| `actionType` | `ActionType \| string`        | Identifies the action. Your own actions use a string of their own.   |
| `title`      | `string`                      | The label. Translate it yourself, as `share` does.                   |
| `icon`       | `ReactElement`                | Optional. The SDK's actions use 20 by 20 icons from the `icons` key. |
| `titleStyle` | `StyleProp<TextStyle>`        | Optional. The style of the label.                                    |
| `type`       | `'standard' \| 'destructive'` | Which group of the menu it renders in.                               |

To change one default action, spread it and replace a field: `{ ...params.deleteMessage, title: "Remove" }` keeps its handler and its confirmation dialog.

### Running code when an action runs

`Channel` and `Thread` also take a handler for each default action, called with the message when the user picks the action, before the SDK's own handler runs. They add to the action, they don't replace it, which makes them the place for analytics or logging:

| Prop                | Called for                               |
| ------------------- | ---------------------------------------- |
| `handleBan`         | `banUser`                                |
| `handleBlockUser`   | `blockUser`, with the user               |
| `handleCopy`        | `copyMessage`                            |
| `handleDelete`      | `deleteMessage`                          |
| `handleDeleteForMe` | `deleteForMeMessage`                     |
| `handleEdit`        | `editMessage`                            |
| `handleFlag`        | `flagMessage`                            |
| `handleMarkUnread`  | `markUnread`                             |
| `handleMute`        | `muteUser`                               |
| `handlePinMessage`  | `pinMessage`, `unpinMessage`             |
| `handleQuotedReply` | `quotedReply`                            |
| `handleRetry`       | `retry`                                  |
| `handleThreadReply` | `threadReply`                            |
| `handleReaction`    | a reaction the user picks, with its type |

```tsx
import type { LocalMessage } from "stream-chat";
import type { Channel as StreamChannel } from "stream-chat";
import {
  Channel,
  MessageComposer,
  MessageList,
} from "stream-chat-react-native";

const track = (event: string, message: LocalMessage) => {
  console.log(event, message.id);
};

const handleDelete = async (message: LocalMessage) =>
  track("delete-picked", message);
const handleFlag = async (message: LocalMessage) =>
  track("flag-picked", message);

export const TrackedChannel = ({ channel }: { channel: StreamChannel }) => (
  <Channel
    channel={channel}
    handleDelete={handleDelete}
    handleFlag={handleFlag}
  >
    <MessageList />
    <MessageComposer />
  </Channel>
);
```

Delete and flag ask for confirmation, and their handlers run before the dialog shows, so they run even when the user cancels. Treat them as "the user picked the action", not "the action happened". A handler set on a `MessageActionType` you build yourself, or an action you call from your own component through `useMessageActionHandlers()`, doesn't call these props.

### Message actions in your own components

Two hooks run a message's actions from a component inside the message, such as a custom `MessageItemView`, one of the `MessageContent*View` slots, or a custom `MessageActionList`. Both take no arguments and read the message from context:

- **`useMessageActions()`** returns the same `MessageActionType` objects the menu shows: `{ banUser, blockUser, copyMessage, deleteForMeMessage, deleteMessage, editMessage, flagMessage, handleReaction, markUnread, muteUser, pinMessage, quotedReply, retry, threadReply, unpinMessage }`. Their `action` runs the `handle*` prop from `Channel` first, as the menu does, and `blockUser` asks for confirmation. Building them subscribes to the author's mute state and translates every title, so the SDK calls this hook only while a menu is open. Don't call it in every row of a list.
- **`useMessageActionHandlers()`** returns the handlers behind the actions, `MessageActionHandlerCallbacks`. `Message` builds them once per message and puts them in `MessageContext` as `messageActionHandlers`, so every caller shares them and calling the hook costs nothing. They don't call the `handle*` props.

| Handler                    | Does                                                                      |
| -------------------------- | ------------------------------------------------------------------------- |
| `handleCopyMessage`        | copies the text, translated into `userLanguage` when a translation exists |
| `handleDeleteMessage`      | asks for confirmation, then deletes the message                           |
| `handleDeleteForMeMessage` | deletes the message for the current user                                  |
| `handleEditMessage`        | puts the message into the composer for editing                            |
| `handleFlagMessage`        | asks for confirmation, then flags the message                             |
| `handleMarkUnreadMessage`  | marks the channel unread from this message                                |
| `handleQuotedReplyMessage` | sets the message as the composer's quoted message                         |
| `handleResendMessage`      | sends a failed message again                                              |
| `handleToggleBanUser`      | bans or unbans the author                                                 |
| `handleToggleBlockUser`    | blocks or unblocks the user you pass, without asking                      |
| `handleToggleMuteUser`     | mutes or unmutes the author                                               |
| `handleTogglePinMessage`   | pins or unpins the message                                                |
| `handleToggleReaction`     | adds or removes the user's reaction of the type you pass                  |

The copy, delete, flag, mark unread, mute and pin handlers publish a notification when they finish or fail: see [Displaying notifications](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/displaying-notifications/#what-the-sdk-publishes).

This one adds a reply button below every message's content:

```tsx
import { Pressable, Text } from "react-native";
import {
  useMessageActionHandlers,
  useTranslationContext,
} from "stream-chat-react-native";
import type { ComponentOverrides } from "stream-chat-react-native";

const ReplyButton = () => {
  const { handleQuotedReplyMessage } = useMessageActionHandlers();
  const { t } = useTranslationContext();

  return (
    <Pressable accessibilityRole="button" onPress={handleQuotedReplyMessage}>
      <Text>{t("message.reply.label", "Reply")}</Text>
    </Pressable>
  );
};

export const replyOverrides: ComponentOverrides = {
  MessageContentBottomView: ReplyButton,
};
```

The same handlers, under shorter names, reach the press handlers: `onLongPressMessage` and `onPressMessage` on `Channel` receive them as `actionHandlers`, together with the message and a `defaultHandler`. `onLongPressMessage={({ actionHandlers }) => actionHandlers?.quotedReply()}` makes a long press quote the message instead of opening the menu.

### Replacing how the menu renders the actions

The action list is the `MessageActionList` key, and each item the `MessageActionListItem` key. `MessageActionList` receives the list your function returned as `messageActions`. `MessageActionListItem` receives one `MessageActionType` as its props. A replacement item closes the menu and runs the action after it closes, as the default does, with `closeOverlay()` and `scheduleActionOnClose(action)`. The menu's other parts are on [Overlays and bottom sheets](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/overlays-and-bottom-sheets/#the-message-menu).

What each default action does, and the actions that come with flagging, muting and reminders, are on [Message actions](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/message-actions/).

## Channel actions

A channel's actions show in three places:

- **A channel-list row's swipe actions.** Swiping a row left reveals a button that opens the row's options sheet, and the actions whose `placement` is `'swipe'` or `'both'`.
- **The options sheet**, `ChannelDetailsBottomSheet`, with the actions whose `placement` is `'sheet'` or `'both'`.
- **Channel details**, in `ChannelDetailsActionsSection`, which ignores `placement`.

`ChannelList` shows the swipe actions unless `swipeActionsEnabled` is `false`. The props the options sheet and its header receive, for a replacement of either, are listed in [The options sheet](https://getstream.io/chat/docs/sdk/react-native/v10/ui/components/channel-list-ui/#the-options-sheet).

The SDK builds the default items with `buildDefaultChannelActionItems`:

| `id`            | Label                         | `placement` | Shown                                                       |
| --------------- | ----------------------------- | ----------- | ----------------------------------------------------------- |
| `mute`          | Mute or Unmute Chat, or Group | `'swipe'`   | always                                                      |
| `pin`           | Pin or Unpin Chat, or Group   | `'sheet'`   | in the channel list only                                    |
| `muteUser`      | Mute or Unmute User           | `'sheet'`   | in a direct message channel                                 |
| `block`         | Block or Unblock User         | `'sheet'`   | in a direct message channel; blocking asks for confirmation |
| `leave`         | Leave Chat, or Group          | `'sheet'`   | always; asks for confirmation                               |
| `deleteChannel` | Delete Chat, or Group         | `'sheet'`   | when the user created the channel; asks for confirmation    |

A direct message channel is one with exactly one member besides the current user. The items don't check the user's capabilities: a user without `leave-channel` sees "Leave", and the request fails with an error notification. Filter the items yourself where your app's permissions need it.

In channel details, `mute` and `muteUser` render as switches, and the others as rows.

### Changing the items

`getChannelActionItems` receives `{ context, defaultItems }` and returns the items to render. `context` holds what the defaults were built from:

| Field                                         | What it is                                                                                                  |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `channel`                                     | the channel                                                                                                 |
| `actions`                                     | `ChannelActions`: the handlers behind the items, see [useChannelActions](#running-channel-and-user-actions) |
| `surface`                                     | `'list'` in the channel list, `'details'` in channel details                                                |
| `isDirectChat`                                | whether the channel has exactly one member besides the current user                                         |
| `isPinned`, `isArchived`, `channelMuteActive` | the channel's state for the current user                                                                    |
| `userMuteActive`, `isBlocked`                 | in a direct message channel, the other member's state                                                       |
| `icons`                                       | the icons in effect, with your overrides                                                                    |
| `t`                                           | the translation function                                                                                    |

This adds an archive action to the options sheet:

```tsx
import type { ChannelPaginator } from "stream-chat";
import { asDynamicKey, ChannelList } from "stream-chat-react-native";
import type {
  ChannelActionItem,
  GetChannelActionItems,
} from "stream-chat-react-native";

const getChannelActionItems: GetChannelActionItems = ({
  context,
  defaultItems,
}) => {
  const { actions, icons, isArchived, t } = context;

  const archive: ChannelActionItem = {
    action: isArchived ? actions.unarchive : actions.archive,
    Icon: icons.Folder,
    id: "archive",
    label: isArchived
      ? t(asDynamicKey("myApp.channel.unarchive.label"), "Unarchive")
      : t(asDynamicKey("myApp.channel.archive.label"), "Archive"),
    placement: "sheet",
    type: "standard",
  };

  return [...defaultItems, archive];
};

export const Channels = ({ paginator }: { paginator: ChannelPaginator }) => (
  <ChannelList
    getChannelActionItems={getChannelActionItems}
    paginator={paginator}
  />
);
```

A `ChannelActionItem` has an `action`, which receives optional `{ onSuccess, onFailure }` callbacks, an `Icon` component that receives `IconProps`, an `id`, a `label`, a `placement` and a `type`, `'standard'` or `'destructive'`. Declare the function at module scope, or wrap it in `useCallback`: the list rebuilds its items when it changes.

To remove an item, filter it out by `id`. To replace one, map over `defaultItems` and keep its `id`: channel details still renders `mute` and `muteUser` as switches.

### In channel details

`ChannelDetails` renders `ChannelDetailsActionsSection` with no props, so it takes your function through an override of that key:

```tsx
import type { Channel as StreamChannel } from "stream-chat";
import {
  ChannelDetails,
  ChannelDetailsActionsSection,
  ChannelDetailsContextProvider,
  WithComponents,
} from "stream-chat-react-native";
import type {
  ChannelDetailsActionsSectionProps,
  ComponentOverrides,
  GetChannelActionItems,
} from "stream-chat-react-native";

// Channel details has no pin item by default. This adds the list's.
const getChannelActionItems: GetChannelActionItems = ({
  context,
  defaultItems,
}) => {
  const { actions, icons, isDirectChat, isPinned, t } = context;
  const pinLabel = isDirectChat
    ? t("channel.pinChat.label", "Pin Chat")
    : t("channel.pinGroup.label", "Pin Group");
  const unpinLabel = isDirectChat
    ? t("channel.unpinChat.label", "Unpin Chat")
    : t("channel.unpinGroup.label", "Unpin Group");

  return [
    ...defaultItems,
    {
      action: isPinned ? actions.unpin : actions.pin,
      Icon: isPinned ? icons.Unpin : icons.Pin,
      id: "pin",
      label: isPinned ? unpinLabel : pinLabel,
      placement: "sheet",
      type: "standard",
    },
  ];
};

const ActionsSection = (props: ChannelDetailsActionsSectionProps) => (
  <ChannelDetailsActionsSection
    {...props}
    getChannelActionItems={getChannelActionItems}
  />
);

const detailsOverrides: ComponentOverrides = {
  ChannelDetailsActionsSection: ActionsSection,
};

export const ChannelDetailsScreen = ({
  channel,
  onBack,
}: {
  channel: StreamChannel;
  onBack: () => void;
}) => (
  <ChannelDetailsContextProvider channel={channel}>
    <WithComponents overrides={detailsOverrides}>
      <ChannelDetails onBack={onBack} />
    </WithComponents>
  </ChannelDetailsContextProvider>
);
```

`ChannelDetailsActionsSection` also takes `onChannelDismiss`, called once the channel is gone for the user, after `leave`, `deleteChannel` or `block` succeeds. Pass it to navigate away from the screen. It reaches the items by `id`, so keep those ids on replaced items.

The same function can serve both places: check `context.surface` to return different items in the list and in details.

### Running channel and user actions

The hooks behind the items run the same actions from your own components:

- **`useChannelActionItems({ channel, surface, getChannelActionItems })`** returns the items for a channel, as the list and details build them. `useChannelActionItemsById` returns them keyed by `id`.
- **`useChannelActions(channel)`** returns `ChannelActions`: `addMembers`, `removeMembers`, `archive`, `unarchive`, `pin`, `unpin`, `muteChannel`, `unmuteChannel`, `muteUser`, `unmuteUser`, `blockUser`, `unblockUser`, `leave`, `deleteChannel`, `updateName` and `updateImage`. The user actions act on the other member of a direct message channel. They don't ask for confirmation.
- **`useUserActions(user)`** returns `UserActions` for any user: `muteUser`, `unmuteUser`, `blockUser` and `unblockUser`.

Every action takes optional `{ onSuccess, onFailure }`, publishes a success or error notification, and resolves without throwing. The `UserActions` call `onSuccess` but never `onFailure`. `addMembers(memberIds, options)`, `removeMembers(memberIds, options)`, `updateName(name, options)` and `updateImage(image, options)` take their value first.

```tsx
import { Pressable, Text } from "react-native";
import type { Channel as StreamChannel } from "stream-chat";
import { useChannelActions } from "stream-chat-react-native";

export const ArchiveButton = ({
  channel,
  onDone,
}: {
  channel: StreamChannel;
  onDone: () => void;
}) => {
  const { archive } = useChannelActions(channel);

  return (
    <Pressable
      accessibilityRole="button"
      onPress={() => archive({ onSuccess: onDone })}
    >
      <Text>Archive</Text>
    </Pressable>
  );
};
```

The hooks publish their notifications through the notification target they sit in. Render them inside the screen whose notification list should show the result: see [Displaying notifications](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/displaying-notifications/).

## Member actions

Pressing a member in channel details opens `ChannelMemberActionsSheet`, with the member's actions. The SDK builds them with `buildDefaultChannelMemberActionItems`:

| `id`           | Label                 | Shown                                                                                |
| -------------- | --------------------- | ------------------------------------------------------------------------------------ |
| `muteUser`     | Mute or Unmute User   | for every member but the current user                                                |
| `block`        | Block or Unblock User | for every member but the current user; blocking asks for confirmation                |
| `removeMember` | Remove User           | for other members, when the user has `update-channel-members`; asks for confirmation |

The sheet takes `getChannelMemberActionItems`, which receives `{ context, defaultItems }` and returns the items. `context` holds `channel`, `member`, `isCurrentUser`, `isBlocked`, `userMuteActive`, `ownCapabilities` (the user's capabilities in the channel, as strings), `channelActions`, `userActions`, `icons` and `t`. A `ChannelMemberActionItem` has the fields of a channel action item without `placement`. Pressing an item runs its `action` and closes the sheet.

Both channel details screens that open the sheet, the member section and the full member list, read it from the `ChannelMemberActionsSheet` key, so one override changes both:

```tsx
import { useCallback } from "react";
import { Linking } from "react-native";
import type { Channel as StreamChannel } from "stream-chat";
import {
  ChannelDetails,
  ChannelDetailsContextProvider,
  ChannelMemberActionsSheet,
  useChannelDetailsContext,
  WithComponents,
} from "stream-chat-react-native";
import type {
  ChannelMemberActionsSheetProps,
  ComponentOverrides,
  GetChannelMemberActionItems,
} from "stream-chat-react-native";

const MemberActionsSheet = (props: ChannelMemberActionsSheetProps) => {
  const { closeModals } = useChannelDetailsContext();

  const getChannelMemberActionItems = useCallback<GetChannelMemberActionItems>(
    ({ context, defaultItems }) => {
      const userId = context.member.user?.id;
      if (context.isCurrentUser || !userId) return defaultItems;

      return [
        {
          action: () => {
            // Close the member list before leaving the screen.
            closeModals();
            void Linking.openURL(`myapp://direct-message/${userId}`);
          },
          Icon: context.icons.MessageBubbleEmpty,
          id: "sendDirectMessage",
          label: "Send Direct Message",
          type: "standard",
        },
        ...defaultItems,
      ];
    },
    [closeModals],
  );

  return (
    <ChannelMemberActionsSheet
      {...props}
      getChannelMemberActionItems={getChannelMemberActionItems}
    />
  );
};

const detailsOverrides: ComponentOverrides = {
  ChannelMemberActionsSheet: MemberActionsSheet,
};

export const ChannelDetailsScreen = ({
  channel,
  onBack,
}: {
  channel: StreamChannel;
  onBack: () => void;
}) => (
  <ChannelDetailsContextProvider channel={channel}>
    <WithComponents overrides={detailsOverrides}>
      <ChannelDetails onBack={onBack} />
    </WithComponents>
  </ChannelDetailsContextProvider>
);
```

`MemberActionsSheet` is declared at module scope and reads what it needs through hooks, so the override object never changes. The sheet closes itself after an item runs. `closeModals()` from `useChannelDetailsContext()` closes the screens channel details opened on top of itself, such as the full member list, so call it before you navigate away.

`useChannelMemberActionItems({ channel, member, getChannelMemberActionItems })` returns a member's items for a sheet of your own, and `useUserActions(member.user)` the user actions alone.

The member list, the roles and adding members are on [Channel details and members](https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/channel-details-and-members/), and the UI cookbooks [Custom channel actions](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/channel-details/custom-channel-actions/) and [Custom member actions](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/channel-details/custom-member-actions/) build complete examples.

## Adding, removing and replacing actions

Start from the default list and change it, rather than writing one from scratch:

- **Remove** an action by filtering it out, by `actionType` for message actions and by `id` for channel and member actions.
- **Add** one by putting an item where you want it. Message actions keep your order within their group, `standard` or `destructive`; channel and member actions keep it as given.
- **Replace** one by keeping its identifier and changing its fields.

An action you add has no condition: it shows on every message, channel or member your function returns it for. Check `params` or `context`, the user's capabilities among them, and leave it out where it doesn't apply. The [Custom message actions](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/message/custom-message-actions/) UI cookbook walks through a complete example.

## Pitfalls

- **Returning the received action objects as they are.** They exist for every message. Start from `defaultMessageActions(params)`, or check the conditions yourself.
- **`messageActions` on `Channel` only.** A `Thread` doesn't inherit it. Pass it to both.
- **A `handle*` prop used as "the action succeeded".** It runs when the user picks the action, before any confirmation and before the request.
- **`useMessageActions()` in every row.** It subscribes and translates for each message. Use `useMessageActionHandlers()` in rows, and keep `useMessageActions()` for a custom menu.
- **`getChannelActionItems` on `ChannelList` expected in channel details.** Channel details takes it through an override of `ChannelDetailsActionsSection`.
- **A replaced `mute`, `leave` or `block` item with a new `id`.** Channel details matches these by `id` for the switches and for `onChannelDismiss`.

Used in: [Message actions](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/message-actions/), [Flagging, muting, blocking and bans](https://getstream.io/chat/docs/sdk/react-native/v10/features/moderation-and-safety/flagging-muting-blocking-and-bans/), [Channel lists](https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/channel-lists/) and [Channel details and members](https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/channel-details-and-members/).

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/actions/](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/actions/).