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. The local member search ignores common accents such as é and ü by default, 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. The kinds other than user need a capability in the channel:

Kind Typed as Notifies Capability Permission in the dashboard
user @<name> one user none none
channel @channel everyone in the channel notify-channel NotifyChannel
here @here members who are online notify-here NotifyHere
role @<role> users with that role notify-role NotifyRole
user_group @<group> members of a user group notify-group NotifyGroup

The message field each kind is sent in is listed in Kinds of mentions. A received message lists its user groups in mentioned_groups.

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 users, @channel and @here, roles and user groups that match "be". It offers only the kinds the user has the capability for. Where the suggestions come from explains each search.
  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 sends each mention that is still in the text with the message.

The middleware and the search source are explained on Mentions.

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. With onMentionsClick, each @name in the text gets cursor: pointer, and the message text can be reached with Tab: pressing Enter or Space on it calls the handler, as a click does. Without it, mentions are highlighted but not clickable. Reacting to clicks and hovers shows a handler. See The input for the composer side.

Turning it on or off

User mentions are always available. Grant the capabilities in the table under What users get 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

To change what is searched, for example to suggest every user of the app or to change the page size, give the mentions middleware a MentionsSearchSource of your own; see Customizing mention search for the options and a sample.

Its transliterate option also lets names written in other scripts match what the user types. Pass a function that returns the text in Latin script, such as the default export of @sindresorhus/transliterate, and typing "@ana" suggests "Анатолий". It applies only to names compared locally; queries to the server aren't transliterated.

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

The search source is replaced in a messageComposer setup function. There is one setup function per kind, so do all composer setup in one; see Setup functions.

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

To do something when the user clicks a mention, such as opening the user's profile, pass onMentionsClick to <MessageList/>, <VirtualizedMessageList/> or <Message/>. onMentionsHover does the same for hovers. Both are called with (event, message).

The handler doesn't say which mention was clicked: it runs for a click anywhere in the message text. Find the mention from the clicked element:

  1. Look up the nearest .str-chat__message-mention from event.target. Without one, the click wasn't on a mention.
  2. Read the mention from the element's data-mention-id and data-mention-type. A user mention also has data-user-id.

Enter or Space on the message text also calls onMentionsClick. Its target is the whole text, so the handler finds no mention and does nothing:

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

const handleMentionClick: CustomMentionHandler = (event, 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} />
);

In a thread, the parent message gets the list's handlers too.

To read a context from inside a mention, such as MessageContext, render a custom mention component through renderText.

Reference