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 above the composer with 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 "Rene" spelled with an accent. - Inserting. Tapping a suggestion inserts it into the text, for example
@Ben. Deleting that text removes the mention. - Highlighting. Mentions in sent messages are colored by kind, and can be pressed.
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":
- The text composer runs its middleware on every change. The mentions middleware finds the
@bebefore the cursor and asksMentionsSearchSourcefor suggestions. - The search source collects them:
- Users. When the channel has fewer than 100 members loaded, it searches its loaded members and watchers locally, by name and id. Otherwise it queries the channel's members by name. With
mentionAllAppUsers, it queries all users of the app instead. @channeland@here, when "be" starts their name.- Roles through
client.searchRoles(), and user groups throughclient.searchUserGroups(), which is scoped to the channel's team. Both need at least one character after@.
- Users. When the channel has fewer than 100 members loaded, it searches its loaded members and watchers locally, by name and id. Otherwise it queries the channel's members by name. With
- The suggestions land in the text composer's state as
suggestions, and the UI lists them. - The user picks Ben. The middleware replaces
@bewith@Benand adds Ben to the text composer'smentions. - 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
AutoCompleteSuggestionList renders the list while the text composer has suggestions. MessageList renders it, above the composer:
AutoCompleteSuggestionHeadertitles the list.- Each row is an
AutoCompleteSuggestionItem, which callsTextComposer.handleSelect()when it's pressed. For@, the row rendersMentionSuggestionItem, which picksMentionUserItem,MentionBroadcastItem(@channeland@here),MentionRoleItemorMentionUserGroupItemby the suggestion'smentionType.
In the message list, renderText finds each mention in the message's text by matching the names in its mention fields, case-insensitively. It renders the mention in its own color: one for users, one for @channel and @here, one for roles and one for user groups. Pressing a mention calls the message's press handler with the textMention emitter. By default that dismisses the keyboard, or, on a message that failed to send or bounced, opens the menu or the bounce alert. See Reacting to presses.
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
Changing the search
Give the mentions middleware a search source with other options, in a composer setup function. This one suggests every user of the app, not only the channel's members:
import type { StreamChat, TextComposerMiddleware } from "stream-chat";
import { createMentionsMiddleware, MentionsSearchSource } from "stream-chat";
export const mentionAllAppUsers = (client: StreamChat) =>
client.config.setSetupFunction("messageComposer", ({ composer }) => {
const { channel, textComposer } = composer;
const searchSource = new MentionsSearchSource(channel, {
mentionAllAppUsers: true,
});
// 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,
]);
};
});Other options of MentionsSearchSource:
transliterateturns the typed text and the names into Latin script before they're compared,(text: string) => string, for example the default export of@sindresorhus/transliterate. It applies where the search source compares names itself: the local member search and@channeland@here. Queries to the server aren't transliterated.memberFiltersreplaces the default member query.userFiltersadds conditions to the user query thatmentionAllAppUsersmakes.memberSortanduserSortreplace the default order.
There is one setup function per kind, so this one replaces any other messageComposer setup function, such as one that calls setupCommandUIMiddlewares. Do all composer setup in one function. See Suggestions.
Replacing a suggestion row
MentionSuggestionItem is an override key. It receives the suggestion as props. This one replaces the user rows and keeps the default for the other kinds:
import { Text, View } from "react-native";
import type { MentionSuggestion } from "stream-chat";
import {
MentionSuggestionItem,
WithComponents,
} from "stream-chat-react-native";
const MentionRow = (item: MentionSuggestion) => {
if (item.mentionType !== "user") return <MentionSuggestionItem {...item} />;
return (
<View style={{ paddingHorizontal: 16, paddingVertical: 8 }}>
<Text>@{item.name ?? item.id}</Text>
</View>
);
};
export const App = () => (
<WithComponents overrides={{ MentionSuggestionItem: MentionRow }}>
{/* the rest of the app */}
</WithComponents>
);AutoCompleteSuggestionList, AutoCompleteSuggestionHeader and AutoCompleteSuggestionItem are override keys too, and replace the list for every trigger. The Custom suggestions UI cookbook builds complete rows.
Styling mentions
A mention's color comes from the semantic color tokens chatTextMentionUser, chatTextMentionBroadcast, chatTextMentionRole and chatTextMentionGroup. The rest of its style, such as the font weight, comes from mentions in messageItemView.content.markdown of the theme. See Theming.
Reacting to presses
onPressMessage and onLongPressMessage on Channel and Thread receive a press on a mention with the textMention emitter. additionalInfo.mentionedEntity is the mention, with its mentionType, and additionalInfo.user is the user for a user mention:
import type { Channel as StreamChannel } from "stream-chat";
import {
Channel,
MessageComposer,
MessageList,
} from "stream-chat-react-native";
export const ChannelWithMentionPresses = ({
channel,
openProfile,
}: {
channel: StreamChannel;
openProfile: (userId: string) => void;
}) => (
<Channel
channel={channel}
onPressMessage={({ additionalInfo, defaultHandler, emitter }) => {
if (emitter === "textMention") {
if (additionalInfo?.user) openProfile(additionalInfo.user.id);
return;
}
defaultHandler?.();
}}
>
<MessageList />
<MessageComposer />
</Channel>
);Reference
- State: Suggestions, Composer middleware and the MessageComposer API.
- UI: Message composer UI and Message list UI.
- Props: Composer UI components, Channel and Message UI components.
- Hooks: Composer hooks and Message hooks.