MessageComposer(
viewModel = composerViewModel,
onMentionSelected = { mention ->
composerViewModel.selectMention(mention)
},
)Mentions
Stream Chat lets a sender notify more than one user at a time. Beyond direct user mentions (@<user>), a message can target everyone in a channel (@channel), only the members currently online (@here), everyone holding a given role (@<role>), or a named user group (@<group>).
The SDK exposes each of these as a Mention subtype, plumbs them through the message composer and the message list, and lets you control which kinds are available per channel via channel capabilities.
Mention kinds
Mention is an interface with five built-in implementations:
| Subtype | Token | Notifies |
|---|---|---|
Mention.User(user) | @<user name> | A single user |
Mention.Channel | @channel | Every member of the channel |
Mention.Here | @here | Online members only |
Mention.Role(role) | @<role> | Every user with that role (up to 10 roles per message) |
Mention.Group(group) | @<group name> | Every member of the named user group (up to 10 groups per message) |
Each kind maps to a server-side channel capability. For the four notify-* kinds, the composer skips that kind in its suggestions when the channel does not return the capability, and trying to send the mention is rejected by the server. User mentions behave differently: the composer always shows user suggestions regardless of create-mention, and the capability is only enforced on send under Permissions V2 (on Permissions V1, regular members lack create-mention yet user mentions still work).
| Capability | Constant | Required for |
|---|---|---|
create-mention | ChannelCapabilities.CREATE_MENTION | User mentions |
notify-channel | ChannelCapabilities.NOTIFY_CHANNEL | @channel |
notify-here | ChannelCapabilities.NOTIFY_HERE | @here |
notify-role | ChannelCapabilities.NOTIFY_ROLE | Role mentions |
notify-group | ChannelCapabilities.NOTIFY_GROUP | Group mentions |
Whether a message actually triggers a push for any given recipient depends on that user's push preferences. See Push Preferences for the full reference.
Sending mentions from the composer
MessageComposer reads the active channel's capabilities and surfaces the matching kinds in the suggestion popup automatically. Typing @ shows users members matching the query plus any of @channel, @here, matching roles, and matching groups that the channel allows. Picking an item inserts the token and remembers the selection; on send, the composer populates the right fields on the outgoing Message.
The only thing to wire up is the selection callback, which receives a Mention:
viewModel.selectMention(mention) is the default, so omitting onMentionSelected works for most apps. Override it when you want to react to the selection (analytics, custom autocomplete behaviour) on top of inserting the token.
Building a message by hand
When sending a message without going through the built-in composer, set the mention fields directly on Message:
val message = Message(
cid = cid,
text = "Heads up @channel, the backup is failing",
mentionedChannel = true,
)
chatClient.channel(cid).sendMessage(message).enqueue()The full set of fields is:
| Field | Type | Meaning |
|---|---|---|
mentionedUsersIds | List<String> | User IDs to ping by name |
mentionedChannel | Boolean | @channel is set |
mentionedHere | Boolean | @here is set |
mentionedRoles | List<String> | Role names to ping |
mentionedGroups | List<UserGroup> | User groups to ping |
Server limits: 100 user IDs (25 on update), 10 roles, 10 groups.
Rendering and click handling
Incoming messages style and linkify every mention token automatically when the matching field on Message is set.
MessageItem, MessageContainer, MessageContent, and MessageText accept an onMentionClick: (Mention) -> Unit callback that fires for every mention kind. When using the higher-level MessageList, wire the handler by overriding ChatComponentFactory.MessageItem:
class MyComponentFactory : ChatComponentFactory {
@Composable
override fun MessageItem(params: MessageItemParams) {
super.MessageItem(
params = params.copy(
onMentionClick = { mention ->
when (mention) {
is Mention.User -> openProfile(mention.user.id)
is Mention.Group -> openGroupInfo(mention.group.id)
is Mention.Role -> openRoleInfo(mention.role)
Mention.Channel, Mention.Here -> Unit
}
},
),
)
}
}
ChatTheme(componentFactory = MyComponentFactory()) {
// ...
}Customizing the suggestion popup
Override the suggestion row via ChatComponentFactory.MessageComposerSuggestionItem. The row gets a Mention and a selection callback; the leading, center, and trailing slots can each be overridden separately.
class MyComponentFactory : ChatComponentFactory {
@Composable
override fun MessageComposerSuggestionItemLeadingContent(
params: MessageComposerSuggestionItemLeadingContentParams,
) {
val icon = when (params.mention) {
Mention.Channel -> R.drawable.ic_megaphone
Mention.Here -> R.drawable.ic_users
is Mention.Role -> R.drawable.ic_role
is Mention.Group -> R.drawable.ic_users
is Mention.User -> R.drawable.ic_person
else -> return
}
Icon(painter = painterResource(icon), contentDescription = null)
}
}
ChatTheme(componentFactory = MyComponentFactory()) {
// ...
}Styling mentions in rendered text
Mention styling goes through MessageTextFormatter. The default formatter accepts a mentionColor lambda for a single colour applied to every mention, or a builder lambda for full control over the AnnotatedString.
ChatTheme(
messageTextFormatter = MessageTextFormatter.defaultFormatter(
autoTranslationEnabled = false,
mentionColor = { isMine -> if (isMine) Color.White else Color(0xFF005FFF) },
),
) {
// ...
}For per-kind styling, pass a builder that walks the annotations and applies a SpanStyle keyed off the mention type tag.