# Emoji

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

Users type emoji with the device keyboard in any app. This page covers what the SDK adds on top: `:shortcode` suggestions in the composer, which are optional and need an emoji search index such as [emoji-mart](https://github.com/missive/emoji-mart)'s, and the larger rendering of messages that hold only emoji. Emoji reactions are covered on [Reactions](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/reactions/).

## What users get

The user types `:` followed by a few letters. After `:piz`, a list suggests the pizza emoji and other matches, each with its name, under the header `Emoji matching "piz"`. Picking one replaces `:piz` with the emoji and a space. When they type an emoticon with a colon that the search knows, such as `:)`, it's replaced with its emoji as they type.

A message that holds only emoji renders them large, without the message bubble.

The SDK has no emoji picker in the composer, so users pick emoji from the keyboard. You can add a row of your own, as [A row of quick emoji](#a-row-of-quick-emoji) shows.

## How it works across the layers

- **State.** An emoji is text. Suggestions come from a text composer middleware, like the ones for mentions and commands, with `:` as its trigger. Picking one writes the emoji into `textComposer`.
- **UI.** `AutoCompleteSuggestionList` shows the suggestions, and `EmojiSuggestionItem` renders each one. `MessageTextContainer` renders an emoji-only message with the larger style.
- **Layout.** Not involved.

### Suggestions

`createTextComposerEmojiMiddleware({ emojiSearchIndex })` builds the middleware. It needs a search index that finds emoji by name: emoji-mart's `SearchIndex`, or any object with a `search(query)` method that returns emoji.

1. On each text change, the middleware looks for a `:` and the text after it, just before the cursor.
2. If the word before the cursor is an emoticon of an emoji the index returns, the word is replaced with that emoji.
3. Otherwise it opens the suggestions with up to seven emoji that the index finds for the text after the colon. An emoji without a glyph, on itself or on its first skin, is left out.
4. Picking one replaces the `:query` with the emoji and a space.

The middleware works like a [custom trigger](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/suggestions/#custom-triggers). The suggestion list renders emoji rows for the `:` trigger.

### Emoji-only messages

When a message's text is made of emoji and spaces only, and the message has no images, files, link cards or Giphy, its text renders with the `onlyEmojiMarkdown` style of the theme, 50 points by default, and the bubble has no background or border. With a visible quoted reply, the bubble stays.

## Turning it on or off

Suggestions are off by default. Install emoji-mart and its data. They aren't dependencies of the SDK:

**RN CLI**

```bash title="Terminal"
yarn add emoji-mart @emoji-mart/data
```

**Expo**

```bash title="Terminal"
npx expo install emoji-mart @emoji-mart/data
```

Initialize emoji-mart's search with its data once, and add the middleware in the composer setup function:

```ts
import data from "@emoji-mart/data";
import { init, SearchIndex } from "emoji-mart";
import type { StreamChat, TextComposerMiddleware } from "stream-chat";
import { createTextComposerEmojiMiddleware } from "stream-chat-react-native";

init({ data });

export const setUpComposers = (client: StreamChat) =>
  client.config.setSetupFunction("messageComposer", ({ composer }) => {
    // One middleware per composer, so composers don't share suggestions.
    const emojiMiddleware = createTextComposerEmojiMiddleware({
      emojiSearchIndex: SearchIndex,
    }) as TextComposerMiddleware;

    composer.textComposer.middlewareExecutor.insert({
      middleware: [emojiMiddleware],
      position: { before: "stream-io/text-composer/mentions-middleware" },
      unique: true,
    });

    return () =>
      composer.textComposer.middlewareExecutor.remove(emojiMiddleware.id);
  });
```

The setup function runs for every composer: channel, thread and edit composers, existing and new. There's one setup function per kind of object, so if your app also installs the [command middleware](https://getstream.io/chat/docs/sdk/react-native/v10/features/rich-content/commands-and-giphy/#install-the-command-middleware), put both in the same function.

The larger rendering of emoji-only messages is always on. Its size is a theme value, set as [The size of emoji-only messages](#the-size-of-emoji-only-messages) shows.

## Customizing

### Your own search index

Pass any object with `search(query)` as `emojiSearchIndex`. It returns emoji, or a promise of them, each with `id`, `name` and `skins` (a list of `{ native }`), and optionally `native`, `emoticons` and `keywords`. The middleware inserts `native`, or the first skin's. This index offers a few emoji of your own:

```ts
import type { Emoji, EmojiSearchIndex } from "stream-chat-react-native";

const teamEmoji: Emoji[] = [
  {
    emoticons: [":)"],
    id: "smile",
    name: "Smile",
    native: "\u{1F604}",
    skins: [],
  },
  { id: "rocket", name: "Rocket", native: "\u{1F680}", skins: [] },
  { id: "tada", name: "Party", native: "\u{1F389}", skins: [] },
];

export const teamEmojiSearchIndex: EmojiSearchIndex = {
  search: (query) =>
    teamEmoji.filter(
      ({ id, name }) =>
        id.includes(query) || name.toLowerCase().includes(query),
    ),
};
```

`createTextComposerEmojiMiddleware` also takes `options`: `minChars`, the shortest `:query` that opens suggestions, counting the colon (1 by default), and `trigger` (`:` by default). The default suggestion list renders emoji rows only for the `:` trigger, so another trigger also needs a list of your own. The `AutoCompleteSuggestionItem` override changes how each suggestion looks, and `AutoCompleteSuggestionHeader` the header, whose "Emoji matching" isn't a translation key. See the [Custom suggestions UI cookbook](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/composer/custom-suggestions/), and the [Emoji suggestions UI cookbook](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/composer/emoji-suggestions/) for the whole setup.

### A row of quick emoji

`MessageComposerTrailingView` renders after the input box, and is empty by default. This row inserts an emoji at the cursor, or in place of the selected text, and keeps the focus in the input:

```tsx
import { Pressable, StyleSheet, Text, View } from "react-native";
import {
  useMessageComposer,
  useMessageInputContext,
} from "stream-chat-react-native";
import type { ComponentOverrides } from "stream-chat-react-native";

const quickEmoji = ["\u{1F44D}", "\u{1F389}", "\u{1F602}", "\u{1F64F}"];

const QuickEmojiRow = () => {
  const { textComposer } = useMessageComposer();
  const { inputBoxRef } = useMessageInputContext();

  const insert = (emoji: string) => {
    textComposer.insertText({ text: emoji });
    inputBoxRef.current?.focus();
  };

  return (
    <View style={styles.row}>
      {quickEmoji.map((emoji) => (
        <Pressable
          accessibilityRole="button"
          key={emoji}
          onPress={() => insert(emoji)}
        >
          <Text style={styles.emoji}>{emoji}</Text>
        </Pressable>
      ))}
    </View>
  );
};

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

const styles = StyleSheet.create({
  emoji: { fontSize: 20, padding: 4 },
  row: { alignSelf: "flex-end", flexDirection: "row", paddingBottom: 8 },
});
```

### The size of emoji-only messages

The style is the theme's `messageItemView.content.textContainer.onlyEmojiMarkdown`, a Markdown style. Pass a smaller one in the `style` of `Chat`:

```tsx
import type { PropsWithChildren } from "react";
import type { StreamChat } from "stream-chat";
import { Chat } from "stream-chat-react-native";
import type { DeepPartial, Theme } from "stream-chat-react-native";

// Declared once at module scope.
const style: DeepPartial<Theme> = {
  messageItemView: {
    content: {
      textContainer: { onlyEmojiMarkdown: { text: { fontSize: 32 } } },
    },
  },
};

export const ChatWithSmallerEmoji = ({
  children,
  client,
}: PropsWithChildren<{ client: StreamChat }>) => (
  <Chat client={client} style={style}>
    {children}
  </Chat>
);
```

The bubble stays without a background at any size. [Theming](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/theming/) explains the theme.

## Reference

- [Suggestions](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/suggestions/): how text middleware opens suggestions.
- [Composer UI components](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-composer/composer-ui-components/): `AutoCompleteSuggestionList` and its items.
- [Composer middleware](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/composer-middleware/): inserting middleware and the setup function.
- [Theme object](https://getstream.io/chat/docs/sdk/react-native/v10/reference/theme/theme-object/): `messageItemView.content.textContainer`.

---

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