Browser Notifications by Emuniq
CommunityEvery ->sendToDatabase() in your app automatically triggers a browser push notification - no code changes needed.
Author:
Emuniq
Package health
Automated checks of this plugin's Composer package
15 checks
- Skipped: GitHub Actions pinned to SHA
- Skipped: GitLab CI includes pinned to SHA
- Passed: Open security advisories
- Passed: Dependabot PR responsiveness — No open Dependabot PRs.
- Skipped: Renovate MR responsiveness
- Skipped: Dependabot or Renovate configured
- Skipped: Dependency update cooldown configured
- Failed: Provides a security policy — View details on Plumb
- Passed: Abandoned or archived — No consulted source marks the package abandoned (packagist, github).
- Passed: Commit and release recency — Active: last commit 87 days ago; last release 87 days ago.
-
Passed:
composer.lock not committed by library
—
composer.lockis absent from the released dist archive. - Passed: Dist archive is lean
-
Passed:
Current Laravel version supported
—
Package dependencies resolve together with current Laravel
13.0. -
Passed:
Current PHP version supported
—
Constraint
^8.1supports current PHP8.5. - Skipped: Current Symfony version supported
filament/
namespace. Review the source and install at your own risk. Found
malware or an unresolved security issue the author won't
address?
Report it
.
Documentation
- Screenshots
- Installation
- How it works
- Features
- Requirements
- iOS Support
- Testing your setup
- Configuration
- Suppressing push for specific notifications
- Translations
- Docker / read-only .env
- Architecture
- License
- Maintained by Emuniq
Every sendToDatabase() in your app automatically triggers a browser push notification — no code changes needed.
#Screenshots
Subscription prompt — appears after a configurable delay, asking the user to enable push notifications.

Profile section (inactive) — auto-injected on the Edit Profile page. One-click to enable.

Profile section (active) — shows subscription status, device count, and a button to disable.

#Installation
composer require emuniq/filament-browser-notifications
php artisan browser-notifications:install
That's it. The install command handles everything:
- Generates VAPID keys and adds them to
.env - Publishes and runs the
push_subscriptionsmigration - Adds the
HasPushSubscriptionstrait to your User model automatically
No manual plugin registration needed — it auto-registers on all Filament panels. No service worker publishing needed — it's served from a route automatically. No profile page changes needed — the subscription management section auto-injects.
Note: The trait step requires the process running
artisanto have write access to your User model (e.g.app/Models/User.php). In setups where the model is read-only for PHP (common in some Docker images), the command skips that step and prints instructions to add the trait by hand — it won't fail the install. To do it manually, add theHasPushSubscriptionstrait to your User model:use NotificationChannels\WebPush\HasPushSubscriptions; class User extends Authenticatable { use HasPushSubscriptions, Notifiable; }
#How it works
Your existing code (unchanged)
|
| Notification::make()->sendToDatabase($users)
| $user->notify(new YourNotification) // with 'database' channel
|
'--> DatabaseNotification::created <-- plugin hooks here automatically
|
+-- Extracts title, body, action URL from notification data
+-- Throttles: groups burst notifications into one push
+-- Dispatches queued job (non-blocking)
|
'--> Web Push via VAPID --> Browser shows native OS notification
|
'--> Click: focuses app tab, navigates to action URL
#Features
Core
- Zero-config piggyback on
DatabaseNotification::created— no per-notification wiring - VAPID-only — no Firebase, no external WebSocket servers, no third-party dependencies
- Queued delivery — push dispatch runs as a background job
- Smart click handling — focuses existing tab and navigates, or opens new tab
Throttle & Grouping
- Burst protection — multiple notifications within a configurable window (default 5s) are grouped into a single "You have N new notifications" push instead of spamming the user
- Configurable via
throttle_secondsin config (set to 0 to disable)
UI
- Non-intrusive subscription prompt — styled Filament section banner, not the raw browser popup
- Dismiss cooldown — "Not now" hides the prompt for 7 days (configurable via localStorage)
- Opt-out respected — if user disables from profile, no re-subscription or prompt until they re-enable
- Profile section auto-injected — subscription management appears on the Edit Profile page automatically
- Dark mode support via native Filament components
Plugin
- Auto-registers on all Filament panels
- Auto-patches User model during install
- Service worker served from route (no asset publishing)
- Panel favicon used as push notification icon
- Dead subscription cleanup (410 Gone auto-deleted by webpush package)
- iOS PWA support — serves
manifest.json, detects iOS Safari, guides users to Add to Home Screen - i18n ready — ships with English and Spanish
- Filament 3, 4, and 5 compatible
#Requirements
- PHP 8.1+
- Laravel 10+ / Filament 3+
- HTTPS (required by the Web Push API)
- A queue worker (Horizon,
queue:work, etc.)
#iOS Support
Push notifications on iOS require the app to be added to the Home Screen as a PWA. The plugin handles this automatically:
- Serves a
manifest.jsonwithdisplay: standaloneand icons from your panel favicon - Injects Apple PWA meta tags
- Detects iOS Safari without PWA and shows "Add to Home Screen" instructions in the profile section
- Once added to Home Screen, the standard VAPID push flow works normally
The manifest's start_url and theme_color are derived from your active Filament panel — the panel path and primary color respectively — so the installed PWA opens the right route with on-brand chrome. Override either one in config/browser-notifications.php if needed:
'manifest' => [
'start_url' => '/backoffice', // null = derive from the panel path
'theme_color' => '#10B981', // null = derive from the panel primary color
],
#Testing your setup
After installation, verify push notifications work:
php artisan browser-notifications:test 1 # queue the test push
php artisan browser-notifications:test 1 --sync # send immediately
#Configuration
Works out of the box. To customize, override the auto-registered plugin in your PanelProvider:
use Emuniq\FilamentBrowserNotifications\BrowserNotificationsPlugin;
->plugins([
BrowserNotificationsPlugin::make()
->promptDelay(5) // seconds before showing prompt (default: 2)
->dismissCooldownDays(14) // days before re-prompting (default: 7)
->profileSection(false), // disable auto-injected profile section
])
Publish the config for queue and throttle settings:
php artisan vendor:publish --tag=browser-notifications-config
// config/browser-notifications.php
return [
'prompt_delay' => 2,
'queue_connection' => null,
'queue_name' => null,
'cleanup_dead_subscriptions' => true,
'throttle_seconds' => 5, // 0 to disable grouping
];
#Suppressing push for specific notifications
Add 'silent' => true via the notification's view data to skip the push (Filament's Notification has no ->data() method — use ->viewData()):
Notification::make()
->title('Low priority update')
->body('...')
->viewData(['silent' => true]) // no browser push for this one
->sendToDatabase($users);
#Translations
Ships with English and Spanish:
php artisan vendor:publish --tag=browser-notifications-lang
#Docker / read-only .env
Generate VAPID keys manually:
php artisan tinker --execute="echo json_encode(\Minishlink\WebPush\VAPID::createVapidKeys());"
Add to .env:
VAPID_PUBLIC_KEY=<publicKey>
VAPID_PRIVATE_KEY=<privateKey>
VAPID_SUBJECT=https://your-app.com
#Architecture
sendToDatabase() / $user->notify()
| DatabaseNotification::created (Eloquent event)
v
SendWebPushOnDatabaseNotification (Listener)
| Checks: has subscriptions? silent? throttle window?
v
SendDatabaseNotificationWebPush (Queued Job, delayed if throttled)
| Counts recent unread notifications
| 1 notification: sends original title/body
| N notifications: sends grouped "You have N new notifications"
v
GenericWebPushNotification -> WebPushChannel -> VAPID -> Push endpoint
v
Service Worker (sw.js) -> showNotification() -> notificationclick -> navigate
#License
MIT
#Maintained by Emuniq
This plugin is built and maintained by Emuniq — a Laravel & Filament consultancy based in Mexico. We help teams ship admin panels, custom Filament resources, and SaaS backoffices.
- 🌐 Website: emuniq.com
- 💼 Need a hand with your Filament project? Get in touch.
- ⭐ If this plugin saves you time, a GitHub star helps others find it.
The author
From the same author
Collapsible Sub-Nav
Adds a clean and responsive collapsible sub-navigation system fully compatible with Filament’s topNavigation() mode.
Author:
Emuniq
Scrollable Top Navigation
Turn the top navigation into a single scrollable row instead of letting it wrap into multiple rows when you have many navigation groups or clusters.
Author:
Emuniq
Featured Plugins
A selection of plugins curated by the Filament team
Compact Theme
A theme that makes data-heavy panels easier to scan by fitting more useful information on each screen.
Filament
Custom Dashboards
Let your users build and share their own dashboards with a drag-and-drop interface. Define your data sources in PHP and let them do the rest.
Filament
Advanced Tables (formerly Filter Sets)
Supercharge your tables with powerful features like user-customizable views, quick filters, multi-column sorting, advanced table searching, convenient view management, and more. Compatible with Resource Panel Tables, Relation Managers, Table Widgets, and Table Builder!
Kenneth Sese