fof/reactions
Add reactions to Flarum posts with fof/reactions. Let users react using emoji or custom icons, with configurable reaction sets, per-post reaction summaries, and a reactions list. Lightweight, extensible, and easy to configure.
Reality check first: fof/reactions is a Flarum extension, not a Laravel package. There is no config/reactions.php, no service provider you register in config/app.php, and no Reaction facade. What you do get is Flarum's stack — which is Illuminate underneath (Eloquent models, migrations, container, event dispatcher, JSON:API resources). If you know Laravel, you already know 60% of this. Treat it as "a Laravel app you can't touch the routes of."
Install (the whole thing, three commands):
composer require fof/reactions
php flarum migrate
php flarum cache:clear
Then enable it in Admin → Extensions → Reactions. Flarum extensions are inert until toggled on — this trips up every first-timer. If the composer install succeeded but nothing appears, it's almost always "not enabled" or "cache not cleared."
Where to look first, in this order:
extend.php — the entire integration surface. Every extender (routes, serializers, permissions, settings, assets) is registered here. Read it before anything else; it's the extension's table of contents.migrations/ — tells you the real data model faster than the models do. You'll see the reactions table and the pivot/post-reaction table.src/ — the Reaction model, the API controller, and the event classes it fires.js/src/ — the Mithril frontend (post footer reaction bar, composer/admin components). This is where 80% of the user-visible behavior lives.js/src/admin/) — reveals every setting key and default.Your first use case: replace a bolted-on "likes" plugin with reactions on posts. Enable the extension, visit the admin panel, add two or three reactions (👍 ❤️ 😮), open a discussion, and react to a post. That single loop — admin config → API write → post-footer render — exercises the whole extension and tells you whether it fits your forum.
1. Treat the reaction set as data, not code. Reactions live in the database as rows (identifier, display value, type, enabled flag), not as an enum in PHP. That means you seed a standard palette per environment instead of hand-clicking the admin UI:
// database/seeders or a Flarum migration
Reaction::firstOrCreate(
['identifier' => 'thumbs_up'],
['display' => '👍', 'type' => 'emoji', 'enabled' => true]
);
Verify the exact column and type names against the migration — this extension has changed its schema across versions.
2. Read reactions through the JSON:API, not raw SQL. Posts serialize their reaction summary so the frontend can render counts without extra round-trips:
GET /api/posts?filter[id]=123&include=reactions
GET /api/discussions/45?include=posts.reactions
If you're building a dashboard, digest email, or Slack notifier, consume that shape. Raw queries against the reactions table will work but will drift from the serialized contract on upgrade.
3. Hook reactions with domain logic via events. This is the Laravel part you'll feel at home with. Register a listener in your own extension's extend.php:
(new Extend\Event())
->listen(PostWasReacted::class, function ($event) {
// award points, notify the author, push to a webhook
})
Check src/ for the exact event class names shipped in your version before wiring this up — events are the least stable part of a small extension's API.
4. Compose with the rest of the FoF ecosystem. Reactions are the input signal for gamification (points/badges), best-answer ranking, and moderation triage. Rather than writing reaction→points logic yourself, let the gamification extension consume it and only add listeners for the gaps. Pattern: reactions = data collection, badges/rank = presentation.
5. Customize the frontend through Flarum extenders, not by forking. For changing where the reaction bar sits or how counts render, override the component from your own extension:
import { extend } from 'flarum/common/extend';
import CommentPost from 'flarum/forum/components/CommentPost';
Forking fof/reactions to tweak a template is the fastest way to a permanent merge conflict.
6. Permission-aware integration. When you surface reaction data elsewhere (search, exports, analytics), respect discussion visibility. Filtering on "post is visible to actor" is your job; the extension only enforces its own endpoints.
cache:clear is not optional. After install, after enabling, after changing settings, after editing extend.php in a local extension. A stale Flarum cache produces "the extension is installed but nothing happens," the single most common issue.js/src, run cd js && npm install && npm run dev (or npm run build for production). PHP-side composer installs do not rebuild assets, and the admin panel will happily serve the old dist/.thumbs_up, deleting that row leaves orphaned reaction records and blank slots in the UI. Disable (enabled = false) instead of deleting. If you must remove one, migrate the reactions to a successor identifier first.settings database table, not in files. Nothing about the reaction configuration is in version control. Document the palette in your deploy notes or seed it, otherwise staging and production will silently diverge.routes/web.php. Console access is php flarum <command>; the container is bootstrapped through Flarum's Site. php flarum info is your php artisan about — start there when the app 500s.fof/reactions in composer.json, upgrade deliberately alongside Flarum core, and check the changelog for schema migrations before running php flarum migrate in production.Authorization: Token <token> before blaming the frontend. Flarum returns JSON:API error documents — read errors[0].detail, not the HTTP status alone. Server-side errors land in storage/logs/flarum-*.log.Reaction model, the JSON:API serializer output, and the settings keys. The Mithril component internals and event class names are the most likely to shift — keep your customizations in your own extension and keep them thin so a version bump is a small diff.How can I help you explore Laravel packages today?