# Mentions

A mention points a message at people: a user, everyone in the channel, everyone online, a role or a user group. The user types `@`, picks from the [suggestions](https://getstream.io/chat/docs/sdk/react-native/state/writing-messages/suggestions/), and the message goes out with the mention, so the server can notify the people it names.

>
> **Info:** **Example**
>
> The user types "Thanks @jo" and picks Cleo from the suggestions:
>
> 1. The text becomes "Thanks @Cleo ", and Cleo is added to the composer's mentions.
> 2. The user sends the message. It goes out with Cleo among its mentioned users, and Cleo is notified.
>
> Had the user deleted "@Cleo" before sending, Cleo wouldn't be mentioned: only mentions still present in the text are sent.
>

## Kinds of mentions

The mentions middleware opens the suggestions after `@`. A picked mention is inserted as `@` and its name, and kept in `textComposer.mentions` (see [Text](https://getstream.io/chat/docs/sdk/react-native/state/writing-messages/text/)). When the message is sent, every mention still present in the text goes with it.

| Mention         | Inserted as    | Offered when the user has | Sent as                   |
| --------------- | -------------- | ------------------------- | ------------------------- |
| A user          | `@Cleo Lee`    | always                    | `mentioned_users`         |
| Everyone        | `@channel`     | `notify-channel`          | `mentioned_channel: true` |
| Everyone online | `@here`        | `notify-here`             | `mentioned_here: true`    |
| A role          | `@admin`       | `notify-role`             | `mentioned_roles`         |
| A user group    | `@Design team` | `notify-group`            | `mentioned_group_ids`     |

The capabilities come from `channel.data.own_capabilities` and are read at each query, so the list follows a change of the user's permissions.

## Where the suggestions come from

- **Users.** When the channel's query loaded all its members (fewer than 100), the members and watchers are searched locally, without a request, and the current user is left out. Larger channels query the members by name. An `@` with nothing typed after it always lists the loaded members and watchers, whatever the channel's size.
- **`@channel` and `@here`** appear on the first page, when they start with what was typed.
- **Roles** are searched with `client.searchRoles()`, and **user groups** with `client.searchUserGroups()`, among the groups of the channel's team when it has one. Both start once something is typed after `@`. Roles appear on the first page only.

Muted users aren't offered. After `/unmute`, only muted users are. If the role or group search fails, the other suggestions still appear.

## Customizing mention search

`MentionsSearchSource` loads the mention suggestions. To change what is searched, give the mentions middleware a search source of your own, created with these options:

| Option                     | What it changes                                                                                                                                                                                                                               |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mentionAllAppUsers`       | Searches every user of the app with `client.queryUsers()`, not only the channel's members. The members and watchers are then never searched locally.                                                                                          |
| `userFilters`              | Filters for the user query, added to the default search by id and name.                                                                                                                                                                       |
| `userSort`                 | The sort of the user query, in place of the default.                                                                                                                                                                                          |
| `memberFilters`            | The filters of the member query, in place of the default.                                                                                                                                                                                     |
| `memberSort`               | The sort of the member query, in place of the default. Only its first entry is used.                                                                                                                                                          |
| `searchOptions`            | Extra options for the user and member queries.                                                                                                                                                                                                |
| `pageSize`                 | How many suggestions a page holds.                                                                                                                                                                                                            |
| `shortQueryMaxLength`      | The longest query that counts as short: 2 characters by default.                                                                                                                                                                              |
| `shortQueryDebounceMs`     | How long a short query waits before it's searched: 500 ms by default.                                                                                                                                                                         |
| `longQueryDebounceMs`      | How long a longer query waits: 300 ms by default.                                                                                                                                                                                             |
| `transliterate`            | A function that normalizes names and queries before the local member search compares them. It runs on top of the built-in step, which already ignores some accents, such as acute accents and the umlaut on `u`. Server queries don't use it. |
| `suggestionFactoryMappers` | Functions that build each kind of suggestion from a user, role, group, `channel` or `here`.                                                                                                                                                   |
| `textComposerText`         | The composer's text. The mentions middleware sets it before each search. When it contains `/unmute`, only muted users are offered. A search source you call yourself, outside the middleware, needs it too.                                   |
| `trigger`                  | The mentions middleware's `trigger`, which the search source uses when it matches names locally. Pass the same one to both.                                                                                                                   |

In this sample, `transliterate` removes accents the built-in step keeps, such as the carons in Czech names, and the search covers every user of the app. Replace the middleware in the composer setup function. `MiddlewareExecutor.replace()` puts the new middleware in the place of the built-in one, because both have the same id:

```ts
import {
  createMentionsMiddleware,
  MentionsSearchSource,
  StreamChat,
} from "stream-chat";
import type { TextComposerMiddleware } from "stream-chat";

const client = StreamChat.getInstance("YOUR_API_KEY");

const removeAccents = (text: string) =>
  text.normalize("NFD").replace(/[\u0300-\u036f]/g, "");

client.config.setSetupFunction("messageComposer", ({ composer }) => {
  const { channel, textComposer } = composer;
  const searchSource = new MentionsSearchSource(channel, {
    mentionAllAppUsers: true,
    pageSize: 20,
    transliterate: removeAccents,
  });

  textComposer.middlewareExecutor.replace([
    createMentionsMiddleware(channel, {
      searchSource,
    }) as TextComposerMiddleware,
  ]);

  return () =>
    textComposer.middlewareExecutor.replace([
      createMentionsMiddleware(channel) as TextComposerMiddleware,
    ]);
});
```

For more control, extend `MentionsSearchSource` and override one of its steps:

| Override                                         | What it does                                                                                                                                                   |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query(searchQuery)`                             | Loads a page and returns `{ items }`.                                                                                                                          |
| `filterQueryResults(items)`                      | Filters a page before it's shown. The default leaves out muted users.                                                                                          |
| `prepareQueryUsersParams(searchQuery, offset)`   | Returns the `filters`, `sort` and `options` of the user query. It's an arrow-function property, not a method, so override it with a property of the same name. |
| `prepareQueryMembersParams(searchQuery, offset)` | Returns the `filters`, `sort` and `options` of the member query. It's an arrow-function property too.                                                          |
| `getStateBeforeFirstQuery(searchQuery)`          | Sets the state a new search starts from. The default keeps the previous items until new ones arrive, so the list doesn't flicker.                              |

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

class WithoutBotsSearchSource extends MentionsSearchSource {
  filterQueryResults(items: MentionSuggestion[]) {
    return super
      .filterQueryResults(items)
      .filter((item) => item.mentionType !== "user" || item.role !== "bot");
  }
}
```

[Suggestions](https://getstream.io/chat/docs/sdk/react-native/state/writing-messages/suggestions/#when-a-list-opens) explains the `minChars` and `trigger` options of the mentions middleware. The SDK's `AutoCompleteSuggestionItem` renders each suggestion, and [Custom suggestions](https://getstream.io/chat/docs/sdk/react-native/ui-cookbooks/composer/custom-suggestions/) shows how to replace it.

## Pitfalls

- **A search source per composer.** Create the search source inside the setup function, as the example does, so composers don't share results.

## Used in

- [Mentions](https://getstream.io/chat/docs/sdk/react-native/features/messaging/mentions/)

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/state/writing-messages/mentions/](https://getstream.io/chat/docs/sdk/react-native/state/writing-messages/mentions/).