Resolvers and mentions
Slack payloads reference workspace entities by ID: <@U024BE7LH> in mrkdwn,
{ type: "user", user_id: "U024BE7LH" } in rich text. The renderer has no
workspace access, so by default it shows the ID (or the payload's own fallback
label). The resolvers prop maps IDs to display values without changing the
payload:
import { BlockKit, type BlockKitInput } from "react-blockkit";
const users: Record<string, string> = { U024BE7LH: "Maya Ito" };
const channels: Record<string, string> = { C024BE91L: "release-train" };
const groups: Record<string, string> = { S0614TZR7: "on-call" };
export function Preview({ data }: { data: BlockKitInput }) {
return (
<BlockKit
data={data}
resolvers={{
user: (id) => users[id],
channel: (id) => channels[id],
usergroup: (id) => groups[id],
}}
/>
);
}Resolvers apply to mentions in both mrkdwn text and rich_text blocks, and
may return any React node: a styled chip, an avatar and name, plain text.
Returning undefined falls through to the default rendering for that mention.
Available resolvers
| Resolver | Signature | Resolves |
|---|---|---|
user | (id: string) => ReactNode | undefined | <@U…> and rich-text user elements |
channel | (id: string) => ReactNode | undefined | <#C…> and rich-text channel elements |
usergroup | (id: string) => ReactNode | undefined | <!subteam^S…> and rich-text usergroup elements |
team | (id: string) => ReactNode | undefined | Rich-text team elements |
emoji | (name: string, unicode?: string) => ReactNode | undefined | :shortcode: tokens and rich-text emoji elements |
date | (timestamp: number, format: string) => ReactNode | undefined | <!date^…> tokens and rich-text date elements |
The renderer always supplies the mention sigil itself (@ for users and
groups, # for channels), so return just the name.
Fallback order
Each mention kind resolves through a fixed chain; the first defined value wins.
- Users, channels, usergroups: your resolver → the payload's fallback
label (the part after
|in<@U024BE7LH|maya>) → the raw ID. - Emoji: your resolver → the payload's explicit
unicodecode point (rich text only) → a built-in glyph table of 36 shortcodes, the setslackmarkemits (:tada:,:warning:,:white_check_mark:, and similar) → the literal:name:text. Unknown and custom emoji names always stay readable. That table is a floor, not full Slack emoji coverage, so supply anemojiresolver if you are rendering arbitrary workspace payloads. - Dates: your resolver → deterministic UTC formatting of Slack's date
tokens (
{date},{date_num},{date_short},{time}, and variants) → the payload's fallback text for relative or unrecognized tokens ({ago}needs a clock, so it intentionally uses the fallback) → an ISO date.
Broadcast mentions (<!here>, <!channel>, <!everyone>) render as styled
@here/@channel/@everyone text and take no resolver.
Example
This message renders with a demo directory wired into user, channel, and
usergroup; emoji and the date token use the built-in fallbacks:
View payload JSON
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<@U024BE7LH> merged the release branch into <#C024BE91L> :tada:\ncc <!subteam^S0614TZR7>. Freeze starts <!date^1784764800^{date_short}|Jul 23, 2026>."
}
}
]
}View payload JSON
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<@U024BE7LH> merged the release branch into <#C024BE91L> :tada:\ncc <!subteam^S0614TZR7>. Freeze starts <!date^1784764800^{date_short}|Jul 23, 2026>."
}
}
]
}The right-hand render is the same payload with no resolvers at all. Each mention falls back to its payload label or raw ID, and nothing disappears.