Info:
This is release candidate documentation for Stream Chat React SDK v15. For the latest stable version, see the latest version (v14).

Mentions

What users get

Typing @ in the composer suggests people and groups to mention. The sent message highlights each mention, and the server notifies the people it names.

  • Suggestions. @ opens a list of channel members, @channel, @here, roles and user groups, narrowed as the user types. When the channel's members are searched locally, common accents are ignored, so "rene" finds "René".
  • Inserting. Picking a suggestion inserts it into the text, for example @Ben. Deleting that text removes the mention.
  • Highlighting. Mentions in sent messages are styled, and can be clicked or hovered.
Typing @rene suggests René

There are five kinds of mention:

Kind Typed as Notifies Message field
user @<name> one user mentioned_users
channel @channel everyone in the channel mentioned_channel
here @here members who are online mentioned_here
role @<role> users with that role mentioned_roles
user_group @<group> members of a user group mentioned_group_ids when sending, mentioned_groups when read

How it works across the layers

State: the text composer

The user types "Thanks @be" in "Design":

  1. The text composer runs its middleware on every change. The mentions middleware finds the @be before the cursor and asks MentionsSearchSource for suggestions.
  2. The search source collects them:
    • Users. When the channel has fewer than 100 members loaded, it searches them locally, by name and id. Otherwise it queries the channel's members by name. With mentionAllAppUsers, it queries all users of the app instead.
    • @channel and @here, when "be" starts their name.
    • Roles through client.searchRoles(), and user groups through client.searchUserGroups(), scoped to the channel's team. Both need at least one character after @.
  3. The suggestions land in the text composer's state as suggestions, and the UI lists them.
  4. The user picks Ben. The middleware replaces @be with @Ben and adds Ben to the text composer's mentions.
  5. When they send, the composer writes each mention to the message field of its kind, from the table above.

Each kind other than user is offered only when the user has its capability in the channel (see Turning it on or off). The capabilities are checked on every search, so the suggestions follow when they change.

The middleware and the search source are explained on Suggestions.

UI: suggestions and highlighting

The input, TextareaComposer, renders the suggestion list while the text composer has suggestions:

  • AutocompleteSuggestionList, by default SuggestionList, lists them and handles the keyboard.
  • Each row is an AutocompleteSuggestionItem. For @, the row renders MentionItem, which picks UserItem, BroadcastMentionItem (@channel and @here), RoleItem or UserGroupItem.

In the message list, the message text is rendered by renderText, which turns each mention into a Mention element:

<span
  class="str-chat__message-mention"
  data-mention-type="user | channel | here | role | user_group"
  data-mention-id="<the mentioned entity's id>"
  data-user-id="<the user's id, for user mentions only>"
>
  @Ben
</span>

MessageList takes onMentionsClick and onMentionsHover. They run when the user clicks or hovers the text of a message that has mentions. See The input for the composer side.

Turning it on or off

User mentions are always available. The other kinds need a capability in the channel:

Kind Capability Permission in the dashboard
channel notify-channel NotifyChannel
here notify-here NotifyHere
role notify-role NotifyRole
user_group notify-group NotifyGroup

Grant them in the Stream Dashboard under Chat Messaging → Roles & Permissions: pick the role (for example channel_member) and the channel type (for example messaging), then turn on the permissions. Until the user has a capability, that kind doesn't appear in the suggestions.

Two kinds also need data on the server:

  • Roles are the roles defined on the same Roles & Permissions page.
  • User groups are named sets of users, mentioned as one with @<group name>. They don't depend on channel membership: a group can be mentioned in any channel where group mentions are allowed. Create and manage them with a server-side SDK; see the User Groups API reference.

Customizing

Give the mentions middleware a search source with other options, in a composer setup function. This one matches the typed text against names written in other scripts, using a transliterate function:

import transliterate from "@sindresorhus/transliterate";
import type { TextComposerMiddleware } from "stream-chat";
import { createMentionsMiddleware, MentionsSearchSource } from "stream-chat";

client.config.setSetupFunction("messageComposer", ({ composer }) => {
  const { channel, textComposer } = composer;
  const searchSource = new MentionsSearchSource(channel, { transliterate });

  // Same id as the default mentions middleware, so it takes its place.
  textComposer.middlewareExecutor.replace([
    createMentionsMiddleware(channel, {
      searchSource,
    }) as TextComposerMiddleware,
  ]);

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

transliterate takes the text and returns it in Latin script, (text: string) => string. It applies where the search source compares names itself: the local member search and @channel and @here. Queries to the server aren't transliterated.

Typing @ana suggests Анатолий

Other options of MentionsSearchSource:

  • mentionAllAppUsers: true suggests every user of the app, not only the channel's members.
  • memberFilters replaces the default member query. userFilters adds conditions to the user query that mentionAllAppUsers makes.
  • memberSort and userSort replace the default order.
  • pageSize sets how many suggestions load at a time.

There is one setup function per kind, so this one replaces any other messageComposer setup function; do all composer setup in one. See Replacing mention search.

Replacing a suggestion row

Pass your own row components to MentionItem, and give the list the new @ row through SuggestionList's suggestionItemComponents:

import type { MentionSuggestion } from "stream-chat";
import type {
  SuggestionListItemComponentProps,
  SuggestionListProps,
  UserItemProps,
} from "stream-chat-react";
import {
  defaultComponents,
  MentionItem,
  SuggestionList,
  UserItem,
  WithComponents,
} from "stream-chat-react";

const UserRow = (props: UserItemProps) => (
  <UserItem {...props} className="user-row" />
);

const MentionRow = (props: SuggestionListItemComponentProps) => (
  <MentionItem
    {...props}
    entity={props.entity as MentionSuggestion}
    UserItem={UserRow}
  />
);

const Suggestions = (props: SuggestionListProps) => (
  <SuggestionList
    {...props}
    suggestionItemComponents={{ ...defaultComponents, "@": MentionRow }}
  />
);

export const App = () => (
  <WithComponents overrides={{ AutocompleteSuggestionList: Suggestions }}>
    {/* the rest of the app */}
  </WithComponents>
);

MentionItem also takes BroadcastMentionItem, RoleItem and UserGroupItem, and SpecialMentionItem for any other kind. The Custom suggestions recipe builds complete rows.

Styling mentions

Each mention has data-mention-type, so CSS can style the kinds differently:

.str-chat__message-mention[data-mention-type="channel"],
.str-chat__message-mention[data-mention-type="here"] {
  color: var(--str-chat__accent-error);
}

To render mentions with your own markup, for example as links to profiles, pass a mention component to renderText; see Custom element rendering.

Reacting to clicks and hovers

onMentionsClick and onMentionsHover receive the event, the message's mentioned users and the message. The second argument is deprecated: read the mention from the clicked element and the message instead. A handler runs for a click anywhere in the text, so check which element was clicked:

import type { CustomMentionHandler } from "stream-chat-react";
import { MessageList } from "stream-chat-react";

const handleMentionClick: CustomMentionHandler = (
  event,
  _mentionedUsers,
  message,
) => {
  const target = event.target as HTMLElement;
  const mention = target.closest<HTMLElement>(".str-chat__message-mention");
  if (!mention) return;

  const { mentionId, mentionType } = mention.dataset;

  if (mentionType === "user") {
    const user = message.mentioned_users?.find(({ id }) => id === mentionId);
    console.log("Open the profile of", user?.name ?? mentionId);
  } else {
    console.log("Clicked a", mentionType, "mention");
  }
};

export const MessageListWithMentionClicks = () => (
  <MessageList onMentionsClick={handleMentionClick} />
);

VirtualizedMessageList doesn't take these props. For it, or to reach other contexts such as MessageContext, use a custom mention component in renderText instead.

Reference