samuelterra22/filament-pwa
Filament PWA adds progressive web app support to Filament panels, letting users install the admin as a standalone app with offline caching, manifest and service worker generation, plus customizable icons and theme colors via simple fluent configuration.
v1.0.0 is the first stable tag (the compare link against v0.0.0 ships with no changelog body), so treat this as the baseline API rather than an upgrade: nothing to migrate, but also nothing documented as "fixed" — pin the version and read the README once before wiring it into anything user-facing.
Minimal path:
composer require samuelterra22/filament-pwaphp artisan vendor:publish --tag="filament-pwa-config" (check php artisan vendor:publish output if the tag differs in this release).use SamuelTerra\FilamentPwa\FilamentPwaPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->id('field')
// ...
->plugin(FilamentPwaPlugin::make());
}
config/filament-pwa.php: app name, short name, start_url, theme/background colours, display: standalone, and the icon set (at minimum 192×192 and 512×512 PNG, plus a maskable variant).php artisan optimize:clear then hard-reload the panel.Where to look first: Chrome DevTools → Application → Manifest (validity + install criteria) and → Service Workers (registration state and scope). Lighthouse's PWA/installability audit is the fastest pass/fail check. If the manifest 404s or the worker never activates, stop there — nothing downstream matters.
First real use case: a Filament panel used by field staff on phones where the win is install-to-home-screen, a branded splash, and a friendly offline page instead of Chrome's dinosaur. That's achievable in an afternoon. Full offline data entry is not — see Gotchas.
Scope the plugin to one panel. Filament apps commonly run admin + an app-facing panel. Only the app-facing one should be installable; enabling it on admin gives you a second install prompt and a second service worker fighting over scope.
Separate cache strategies by route class. The useful mental model is three buckets:
/livewire/, login/logout, password reset, and any authenticated HTML response.If the published service worker stub is editable, encode that explicitly rather than relying on a default. If it isn't, be conservative in config and verify with DevTools → Network that authenticated pages aren't served (from ServiceWorker).
Bundle the offline page with the panel's own theme so the fallback doesn't look like a foreign site. A Blade view published from the package and overridden in resources/views/vendor/... is the usual extension point.
Deployment pipeline. Every release that changes assets needs the service worker's cache name/version bumped, otherwise returning users keep the old shell. Make it part of the deploy script:
php artisan optimize:clear
php artisan filament:assets
# bump the SW cache version constant, or rely on the package's build step
If the package's worker calls skipWaiting() + clientsClaim(), users get the new shell on next navigation with no prompt — convenient, but it can swap the page out from under someone mid-form. If it doesn't, add an update-available toast and let the user refresh.
Install prompt as a Filament action. Capture beforeinstallprompt, stash the event, and surface it via a panel render hook or a user-menu action ("Install app"). On iOS Safari there's no such event — show a static "Share → Add to Home Screen" hint instead. Don't show the prompt on desktop Chrome unless the panel is genuinely desktop-viable.
Pair with Filament's SPA/navigate mode carefully. wire:navigate and a service worker both intercept navigation; test that back/forward and deep links to a cold-loaded panel still resolve, particularly when the network is throttled to Offline.
/field/sw.js only controls /field/*. Registering from a deeper path silently narrows control. If the panel lives on a subdomain, that's cleaner; on a subpath, verify the Service-Worker-Allowed header or root-level serving.sw.js and manifest.json must not be long-cached. A CDN or Cache-Control: max-age=31536000 on the worker means you can never ship an update. Exclude both paths from your cache rules and from Laravel's response caching middleware./livewire/update calls cached or replayed cause phantom validation errors and stale state.npm run dev is running will serve production assets over your HMR session. Unregister during development, or scope the plugin's registration to app()->isProduction().php artisan config:cache bakes the manifest values in; editing config/filament-pwa.php on a cached production box appears to do nothing. Clear cache as part of every config change.chrome://serviceworker-internals for unregister/stop control, DevTools → Application → Cache Storage to inspect what's actually stored, and Lighthouse in an incognito window (extensions and existing registrations skew results).FilamentPwaPlugin::make(). Exhaust those before vendoring — the API is one major version old and will likely move.1.0.0, there's no signal about what's stable vs. incidental. Constrain to ^1.0 in composer.json, and re-run the installability audit on every minor bump rather than assuming silence means no change.How can I help you explore Laravel packages today?