Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Notifier Laravel Package

usamamuneerchaudhary/filament-notifier

A powerful multi-channel notification system for FilamentPHP with template management, scheduled delivery, event triggers, user preferences via REST API, and an analytics dashboard. Supports email, SMS, Slack, Discord and push, with built-in rate limiting and open/click tracking.

View on GitHub
Deep Wiki
Context7

Getting Started

Pin before you install. With 36 stars and zero dependents, this is a young package — treat the version constraint like a production dependency, not a convenience. Check compatibility first:

composer require usamamuneerchaudhary/filament-notifier --dry-run
composer show usamamuneerchaudhary/filament-notifier

Confirm the Laravel and Filament major versions it supports before committing. Then:

composer require usamamuneerchaudhary/filament-notifier
php artisan notifier:install
composer dump-autoload   # required for the package facades to register

Review what the installer actually did before running it against a shared database. It publishes config/notifier.php, runs migrations, and seeds sample channels and templates. On a fresh local DB that is fine; on staging/production, diff the migrations, and delete the sample records from the Filament resources afterward so they don't show up in your users' preferences API.

Register the plugin in your panel provider — this is what gives you the dashboard, settings pages, and the channel/event/template resources:

use Usamamuneerchaudhary\Notifier\FilamentNotifierPlugin;

public function panel(Panel $panel): Panel
{
    return $panel->plugins([
        FilamentNotifierPlugin::make(),
    ]);
}

Where to look first, in order:

  1. config/notifier.php — the events map, channel enable flags, rate limits. This file is the real API surface; read it end to end.
  2. The Filament Notifier → Settings pages — rate limiting and analytics toggles live in the DB, not in config, and they override your expectations.
  3. php artisan list notifier — confirm which commands actually exist (scheduled send, analytics cleanup) rather than assuming from the README.
  4. route:list | grep notifier — the preferences API and tracking endpoints are registered by the package; verify guards and prefixes match your app.

First use case — welcome email on registration. Do not start with Slack/Discord/push. Prove the whole loop (template → event → channel → delivery → record) with email first:

  1. Create a template in the Filament resource with key welcome-email, type email, and variables name, app_name.
  2. Add the event to config/notifier.php:
'events' => [
    'user.registered' => [
        'channels' => ['email'],
        'template' => 'welcome-email',
    ],
],
  1. Fire it once from Tinker to validate before wiring business logic:
use Usamamuneerchaudhary\Notifier\Facades\Notifier;

Notifier::send($user, 'user.registered', [
    'name'  => $user->name,
    'email' => $user->email,
]);
  1. Set MAIL_MAILER=log locally, check storage/logs/laravel.log, then inspect the notifications table row for status. Only after that, move the call into your registration listener.

Implementation Patterns

Prefer event-driven for anything business-owned; use the facade for one-offs. The events config keeps channel routing and template selection in one reviewable place, which is exactly what you want when a product manager asks "what does this notification do?". Reserve Notifier::send() for ad-hoc, admin-triggered, or test sends. Avoid scattering event keys as string literals across the codebase — wrap them in an enum or constants class, because a typo in an event key or template key fails quietly rather than loudly.

Send after the transaction commits. The classic bug with any notification package: a user is created inside a DB transaction, the notification fires, the transaction rolls back, and the recipient gets a welcome email for an account that doesn't exist. Dispatch from a queued listener with afterCommit, or call the notifier from an Event/Listener pair that runs post-commit:

class SendWelcomeNotification implements ShouldQueue
{
    public $afterCommit = true;

    public function handle(UserRegistered $event): void
    {
        app('notifier')->send($event->user, 'user.registered', [
            'name' => $event->user->name,
        ]);
    }
}

Keep template variables flat and scalar. {{name}}, {{app_name}}, {{order_id}}. Nested arrays and objects are where template engines get inconsistent, and the README doesn't promise a specific rendering behavior. Build the payload in one place per event so every call site passes the same keys — a missing variable is usually a silently blank email, not an exception.

Use dependency injection in your own services, facades at the edges. The README explicitly recommends this, and it is the right call: PreferenceService and AnalyticsService injected into constructors are trivially mockable, while the facades resolve from the container and are best confined to controllers, listeners, and Blade.

Preferences API is a product feature, not plumbing. The endpoints under /api/notifier/preferences give you a ready-made opt-in/opt-out UI backend. Pattern worth adopting:

  • Call GET /api/notifier/preferences/available once and cache the event/channel catalogue on the frontend; it changes only when you change events or channels.
  • Render toggles from channels, not from a hardcoded list — new channels appear without a frontend release.
  • Handle 403 explicitly. If an admin flips allow_override off, every PUT starts returning 403. That should render as "your administrator manages these settings", not a generic error toast.
  • Remember the response is per-event; there is no bulk update endpoint, so a "save all" UI is N sequential PUTs. Debounce it.

Rate limiting is a safety net for bulk work, not a per-user fairness system. Before you write a backfill, import, or announcement loop, read the settings page values and chunk accordingly. The dashboard widget shows real-time minute/hour/day usage — useful during a rollout, but don't leave auto-refresh polling on in a high-traffic admin panel.

Custom channels are a clean seam. If you need Teams, webhooks, or an internal event bus, implement ChannelDriverInterface::send() and validateSettings() and keep the transport logic entirely in the driver. Put the credentials in the channel's settings record rather than in .env where possible, so ops can rotate them from the UI. Verify how the driver is registered (config map vs. discovery) against the source before you plan around it.

Analytics: opt in deliberately. Open and click tracking injects a pixel and rewrites URLs. That is the right default for marketing-style notifications and the wrong default for transactional security emails (password resets, magic links, anything with a signed URL). Enable track_opens/track_clicks per deployment with that distinction in mind, and schedule the retention job:

// routes/console.php or Kernel
$schedule->command('notifier:cleanup-analytics')->daily();

Run it once with --dry-run first to see exactly what it will anonymize.

Gotchas and Tips

Maturity risk is the headline gotcha. Zero dependents and 36 stars means you are among the first serious users. Pin the exact version, read the diff of every upgrade, and write your own integration tests around the send path — template rendering, channel dispatch, preference resolution. If it breaks, you are debugging source, not Stack Overflow.

Two switches per channel. Channel enable flags exist in .env/config and channel records exist in the database. A notification silently not going out is almost always one of those being off, or the template's type not matching the channel type. Check both before touching code.

Config cache will eat your event changes. After editing the events array, run php artisan config:clear. In a containerized deploy, remember config is cached at build time — a stale image will happily use the old event map.

Facades 500 immediately after install. "Class not found" on Notifier:: is the documented composer dump-autoload step. In Docker, that means rebuilding the image or running it in the container, not on the host.

Scheduling needs infrastructure you probably haven't verified. Notifier::schedule() writes intent; something must pick it up. Confirm the package ships a scheduled-send command (check php artisan list notifier), then confirm your cron runs schedule:run every minute, and that a queue worker is alive. Also check how the scheduled time is stored/compared — timezone bugs here produce notifications that appear hours late, and "late" is much harder to notice than "missing".

Tracking pixel inflates open rates. Apple Mail Privacy Protection and corporate image proxies prefetch pixels, so opens_count overstates real engagement. Treat opens as directional and clicks as the trustworthy signal. Also verify that click rewriting doesn't break unsubscribe links, signed URLs, deep links, or mailto: — test with a real client, not just a browser.

Analytics must be off in tests. A test suite that sends real mail through this package will generate tracking records, hit the tracking endpoints, and potentially trip rate limits, creating flaky tests that fail only on the fifth run. Disable analytics and rate limiting in phpunit.xml env, or fake the underlying mail transport.

Rate limits use your cache store. If the limiter is cache-backed, CACHE_STORE=array in a multi-node setup means every node has its own counter — the limits are effectively multiplied by your node count. Use Redis or the database store, and confirm the limiter is global rather than per-user (if it's global, one noisy tenant can starve everyone).

Preference API auth is your responsibility to verify. Run route:list and check which middleware guards the routes. Packages frequently assume the default web/sanctum guard; if your app uses a custom guard or multi-tenant scoping, users may read or write another tenant's preferences. Test cross-user access explicitly — this is a data leak class of bug, not a cosmetic one.

Template variable escaping is undocumented. Before you interpolate anything user-controlled into an HTML email (display names, ticket subjects, order notes), read the renderer and confirm escaping. Assume nothing until you've thrown <script> into a variable and inspected the output.

Partial channel failure is the debugging trap. A notification with channels: ['email', 'slack'] can succeed on one and fail on the other. When someone says "I didn't get it", inspect the notification record per channel, then the Laravel log — don't debug from the top-level send return value. Add monitoring/alerting on the failed-status count that the dashboard widget already exposes.

The installer is not idempotent-friendly until proven otherwise. Re-running notifier:install in CI or on a second environment is a common source of duplicate sample templates and channels. Guard it, or replace the seeded data with your own seeder.

Cleanup before go-live checklist: remove sample channels/templates, decide allow_override, set rate limits to something you've actually load-tested, schedule notifier:cleanup-analytics, turn off widget auto-refresh if the admin panel is busy, and set a calendar reminder to re-read the changelog before the next minor bump.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings