flarum/mentions
Flarum Mentions adds @mention support to your forum. Type @ to autocomplete users, link to their profiles, and notify mentioned members through Flarum's notification system. It works in posts and replies, making discussions more engaging and helping people quickly spot when they're referenced.
flarum/mentions is not a typical third-party package: it is a bundled Flarum extension that ships with core and is enabled by default, so in most installs you are extending it rather than installing it. If it has been removed, pull it back in and enable it:
composer require flarum/mentions
php flarum extension:enable flarum-mentions
Where to look first, in vendor/flarum/mentions/:
extend.php — the extension's wiring: formatter configurators, event listeners, notification blueprints, API routes, and frontend bootstrapping. Read this before anything else.src/Formatter/ — the s9e TextFormatter configurators that parse and render <USERMENTION>, <POSTMENTION>, and <GROUPMENTION> tags.src/ models/listeners — the PostMention / UserMention / GroupMention persistence layer and the listeners that populate it from post content.js/src/forum/ — autocomplete, the reply-to-post button, mention rendering, and the profile "Mentions" tab.migrations/ — the mention tables (e.g. post_mentions, user_mentions).First practical use case: trace one mention end to end so you understand the flow before customizing it.
@ and pick a user; inspect the raw post content — you'll see a USERMENTION tag with id, username, and displayname attributes. Replies produce a POSTMENTION plus a blockquote.Flarum\Post\Event\Posted that dumps $post->mentions — that relationship is the developer-facing entry point for almost all server-side work.Once that flow is clear, the natural next step is either (a) reacting to mentions (webhooks, Slack, email, audit logs) or (b) adding a custom mention type (e.g. @ticket, @product) — the latter is the extension's biggest extension point and the main reason developers touch it day-to-day.
Reacting to mentions server-side. Mentions are persisted as rows, so treat events as the trigger and the relationships as the data:
// extend.php
(new Extend\Event())
->listen(Flarum\Post\Event\Posted::class, function ($event) {
$event->post->loadMissing('mentions');
foreach ($event->post->mentions as $user) {
// dispatch to Slack, email, audit log, etc.
}
});
Mirror this for Revised if edits can add or remove mentions, and be aware that a mention can also be created via reply (POSTMENTION) without any @ typed by the author.
Querying mentions. The mentions relationship on posts and mentionedBy on users are your primary handles, and they're available over the API as relationships. For the profile "Mentions" tab style of view, the frontend resolves posts via the mentions relationship/filter rather than a bespoke endpoint — reuse that rather than building a parallel query layer.
Building a custom mention type. This is the highest-leverage pattern and touches every layer:
<TICKETMENTION id="..." displayname="..."/>) in the parser/renderer configurator so it survives the round trip through post content. Follow the existing USERMENTION configurator's structure exactly — attribute validation, unparse rules, and tag registration.Posted/Revised that reconciles rows from the parsed tags (insert new, delete removed).js/src/forum/, and a renderer/component so the tag displays as a clickable chip rather than falling back to plain text.Do all five, in that order; skipping the formatter step is the most common cause of "the tag disappears after save."
Notification customization. The extension ships notification blueprints for user, group, and post mentions. The usual pattern is to subclass or replace a blueprint to change the alert text, add email support, or point the email template at your own view:
getEmailTemplate() / alert types.NotificationWillBeSent to add routing, digests, or suppression (e.g. don't email if the mentioned user is currently online).Frontend integration tips.
flarum/mentions components rather than re-implementing tag syntax.@ trigger boundary so you don't create a second competing dropdown.Config quirks. There is very little runtime configuration — most behavior is compile-time (formatter + bootstrapping). If you need configurable behavior (mentions per post cap, notification throttle), add it as an extension setting rather than editing vendor code, and read it in a listener.
Post content is the source of truth; mention rows are derived. The USERMENTION tags inside the post's XML content are what the formatter parses. If you programmatically rewrite post content (importers, migrations, bots), you must preserve or regenerate those tags — otherwise the rows drop out on the next parse and notifications stop firing.
Stale display names are normal. Rows store displayname/username snapshots alongside the ID. Renaming a user doesn't rewrite historical content, so raw content can disagree with the current name; rendering resolves by ID. Don't build features that depend on the stored display name being current — join to the user.
Deletion leaves dangling mentions. Deleting a user or post can orphan mention rows and queued notifications. Add cascading foreign keys in your own mention tables, and guard with ?-> / whereHas in any listener that iterates mentions — a deleted user mid-fan-out is a classic null crash.
Replies create mentions you didn't type. Quoting a post via reply generates a POSTMENTION to the quoted author. Programmatically copying or quoting large post bodies can carry nested mention tags with them, producing surprise notifications — strip mention tags when you're transforming content rather than quoting it.
Notification fan-out is unbounded. A post mentioning twenty users creates twenty notifications synchronously. On busy forums, queue the fan-out and consider a per-post or per-user throttle. Cap mention counts in the composer/parser if your community is prone to @-spam.
Formatter changes require a cache clear. After touching configurators or adding tags, run php flarum cache:clear. The formatter config is cached, and a half-applied change produces tags that render as nothing or as raw text — the classic symptom of a stale cache rather than a bug.
Custom tags must be registered in both parse and render paths. Registering a tag for parsing but not rendering (or vice versa) yields content that saves cleanly and then silently disappears. Test by saving, reloading, and inspecting the stored content, not just the rendered page.
@ inside code blocks and email addresses. s9e handles most of this, but custom mention syntaxes are easy to make too greedy. If you add a new mention type, verify it doesn't trigger inside fenced code, inline code, or ordinary email text.
Mention visibility follows discussion permission. A mentioned user who can't see a private discussion will get a broken or suppressed notification. If you're surfacing mentions outside Flarum (email digests, Slack), re-check visibility before you leak post content.
Editing is the tricky path. Revised can both add and remove mentions. Reconcile (diff against existing rows) rather than blindly inserting, or you'll accumulate duplicates and double-notify on every edit.
How can I help you explore Laravel packages today?