# Commands and Giphy

> **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/).

Commands are messages that start with a slash, such as `/giphy cats` or `/mute @bob`, which the server runs instead of posting as text. This page covers how users pick a command in the composer, what has to be installed for a picked command to be sent, and the Giphy preview.

## What users get

The user types `/` at the start of the input. A list titled "Instant Commands" shows the commands of the channel's type, each with its name and its arguments, such as `/giphy [text]` and `/ban [@username] [text]`. The attachment picker has the same list behind its commands button. When they pick `giphy`, the composer shows a GIPHY chip before the input and empties itself. If they remove the chip with its close button, the files and other content they had added come back. The text they typed to find the command doesn't. Had they picked it from the attachment picker, their text would come back too. While the chip shows, the attach button is hidden and the send button shows a checkmark.

They type `cats` and send. A Giphy preview appears in the list, marked "Only visible to you", with three buttons: Send posts the GIF to the channel, Shuffle shows another one, and Cancel removes the preview.

Some commands need more before they can be sent: `/ban`, `/mute`, `/unmute` and `/unban` need a mentioned user, and `/ban` a reason too. Sending one too early shows a warning, and nothing is sent. No command can be used while a message is being edited, and moderation commands can't be used in a reply that quotes a message. A disabled command shows greyed out in the list.

## How it works across the layers

- **State.** The channel type's commands are in `channel.config.availableCommands`. The text composer's commands middleware suggests them after `/` and keeps the picked one in `textComposer.command`. The server runs a command when a message's text starts with it.
- **UI.** `AutoCompleteSuggestionList` shows the suggestions, the attachment picker's commands button shows the same list, and `InputView` shows the chip next to the input. In the message list, `Giphy` renders the preview with its buttons.
- **Layout.** Not involved.

### Picking a command

1. The user types `/gi`. The commands middleware matches `gi` against `availableCommands`, locally and without a request, and opens the suggestions.
2. They pick `giphy`. `textComposer.command` is set, and the `command.activate` effect saves what the composer held and empties it, keeping only the command. From the attachment picker, the SDK calls `TextComposer.setCommand(command)` and focuses the input.
3. They type `cats`. While a command is active, mentions still work, so `/mute @bob` can be completed.
4. On send, the composer validates the command (see [Suggestions](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/suggestions/#commands)) and sends the message. Removing the chip instead calls `TextComposer.clearCommand()`, which brings back what was saved.

For step 4 to send `/giphy cats` rather than `cats`, the command name has to go back in front of the text. Four command middleware do that, and the SDK doesn't install them. See [Install the command middleware](#install-the-command-middleware).

### The Giphy preview

1. The server answers `/giphy cats` with an ephemeral message: `type: "ephemeral"`, `command: "giphy"`, and an attachment of type `giphy` with three actions. Only the user receives it, and it takes the place of their sent message in the list.
2. `Attachment` renders it with `Giphy`, which shows the GIF and a button for each action. A message whose attachment has actions shows no text, and can't be long-pressed for the message menu.
3. Each button calls the message's `handleAction(name, value)`, which calls `Channel.sendAction(message.id, { [name]: value })` with the action's value: `send`, `shuffle` or `cancel`.
4. When the response carries a message, it replaces the preview: another GIF after Shuffle, the posted message after Send. After Cancel it carries none, and the preview is removed from the list.

The GIF shows the `giphyVersion` rendition, `fixed_height` by default. Set it on `Channel` and `Thread` for the message list, and on `OverlayProvider` for the image gallery.

## Turning it on or off

### Which commands exist

The server decides. Each channel type has a list of commands, set in the [Stream Dashboard](https://getstream.io/signin/?product=chat) or through the server API. The client can't add to it. `channel.config.availableCommands` is the server's list as it is. With an empty list, no suggestions open and the attachment picker has no commands button.

### Install the command middleware

The SDK sends a picked command without its name. This is what it sends, with and without the four command middleware:

| The user enters                                                           | Without the middleware                              | With them                                             |
| ------------------------------------------------------------------------- | --------------------------------------------------- | ----------------------------------------------------- |
| `/giphy cats`, typed in full                                              | `/giphy cats`. The chip shows beside the full text. | `/giphy cats`. The text shows `cats` beside the chip. |
| `giphy` picked from the suggestions or the attachment picker, then `cats` | `cats`, as a plain message. No GIF.                 | `/giphy cats`                                         |

A draft saved while a command is active loses the command the same way. `setupCommandUIMiddlewares(composer)` inserts all four. Call it in the composer setup function, and remove them in the teardown:

```ts
import type { StreamChat } from "stream-chat";
import { setupCommandUIMiddlewares } from "stream-chat-react-native";

export const setUpComposers = (client: StreamChat) =>
  client.config.setSetupFunction("messageComposer", ({ composer }) => {
    setupCommandUIMiddlewares(composer);

    return () => {
      composer.compositionMiddlewareExecutor.remove(
        "stream-io/message-composer-middleware/command-string-injection",
      );
      composer.draftCompositionMiddlewareExecutor.remove(
        "stream-io/message-composer-middleware/draft-command-string-injection",
      );
      composer.textComposer.middlewareExecutor.remove([
        "stream-io/text-composer/active-command-guard",
        "stream-io/text-composer/command-string-extraction",
      ]);
    };
  });
```

Register it once, for example right after the client is created. It applies to the composers that exist and to every one created later. Only the first of the four is inserted with `unique: true`, so without the teardown, a setup function that runs again installs the other three twice. There's one setup function per kind of object, so if your app already has a composer setup function, such as the one for [emoji suggestions](https://getstream.io/chat/docs/sdk/react-native/v10/features/rich-content/emoji/#turning-it-on-or-off), add these calls to it. [Composer middleware](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/composer-middleware/#command-middleware) explains each middleware.

### Hide commands in the UI

`hasCommands={false}` on `Channel` and `Thread` hides the attachment picker's commands button. The suggestions after `/` come from the commands middleware, so remove it in your composer setup function:

```ts
import type { StreamChat } from "stream-chat";

export const hideCommandSuggestions = (client: StreamChat) =>
  client.config.setSetupFunction("messageComposer", ({ composer }) => {
    composer.textComposer.middlewareExecutor.remove(
      "stream-io/text-composer/commands-middleware",
    );
  });
```

The server still runs a command a user types in full, so to turn a command off for good, remove it from the channel type.

## Customizing

### When a command can be sent

`messageComposer.commands.sendValidator` decides whether the active command is ready, for example to require a search term after `/giphy`. [Suggestions](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/suggestions/#commands) has the example.

### The command chip

The chip has no override key of its own. `InputView` renders it next to the text input, `AutoCompleteInput`, and the also-send-to-channel checkbox of a thread under them. This `InputView` shows the active command as a line of text above the input, which clears it when tapped:

```tsx
import { Pressable, StyleSheet, Text, View } from "react-native";
import type { TextComposerState } from "stream-chat";
import {
  AutoCompleteInput,
  ShowThreadMessageInChannelButton,
  useMessageComposer,
  useStateStore,
} from "stream-chat-react-native";
import type {
  ComponentOverrides,
  InputViewProps,
} from "stream-chat-react-native";

const commandSelector = ({ command }: TextComposerState) => ({ command });

const InputViewWithCommandLine = (props: InputViewProps) => {
  const { textComposer } = useMessageComposer();
  const { command } = useStateStore(textComposer.state, commandSelector);

  return (
    <View style={styles.container}>
      {command ? (
        <Pressable
          accessibilityLabel={`Remove /${command.name}`}
          onPress={() => textComposer.clearCommand()}
        >
          <Text style={styles.command}>/{command.name}</Text>
        </Pressable>
      ) : null}
      <AutoCompleteInput {...props} />
      <ShowThreadMessageInChannelButton />
    </View>
  );
};

// Pass to `WithComponents`, declared once at module scope.
export const overrides: ComponentOverrides = {
  InputView: InputViewWithCommandLine,
};

const styles = StyleSheet.create({
  command: { fontWeight: "600", paddingHorizontal: 8 },
  container: { flex: 1 },
});
```

The [Commands UI cookbook](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/composer/commands-ui/) builds a fuller command UI.

### Command suggestions

`AutoCompleteSuggestionList`, `AutoCompleteSuggestionHeader` and `AutoCompleteSuggestionItem` replace the suggestion list, its header and its items. `CommandSuggestionItem` is the SDK's command row, if you want to reuse it. The header's "Instant Commands" isn't a translation key, so replace `AutoCompleteSuggestionHeader` to change it. The [Custom suggestions UI cookbook](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/composer/custom-suggestions/) shows the list and its items.

### Replacing the Giphy preview

`Giphy` is an override key, and receives the `attachment`. A Giphy of your own reads `handleAction` from `useMessageContext()` and calls it with an action's `name` and `value` to run it, the way the SDK's buttons do. Without `actions`, the attachment is a posted GIF.

## Reference

- [Attachments](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message/attachments/): `Giphy` and the `Attachment` component.
- [Composer UI components](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/composer-ui-components/): `InputView`, `AutoCompleteInput` and the composer's state while a command is active.
- [Attachment picker](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/attachment-picker/): the commands button and `AttachmentCommandPicker`.
- [Channel](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/channel/channel/): `hasCommands` and `giphyVersion`.
- [Composer middleware](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/composer-middleware/) and [Suggestions](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/suggestions/): the text middleware behind commands.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v10/features/rich-content/commands-and-giphy/](https://getstream.io/chat/docs/sdk/react-native/v10/features/rich-content/commands-and-giphy/).