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 Pwa Laravel Package

jeffersongoncalves/filament-pwa

Filament PWA adds Progressive Web App support to Filament panels, making admin apps installable and more app-like with a web app manifest, service worker integration, and customizable PWA settings for Laravel projects.

View on GitHub
Deep Wiki
Context7

Getting Started

What you are actually installing. Before running composer, internalize the scope: this package injects <head> markup — manifest link, theme-color, Apple touch icons, msapplication-* tiles, favicons — into a Filament panel via render hooks. It does not ship a service worker, offline caching, push notifications, or install-prompt handling. Plan your first use case as branding plus home-screen install, not offline admin.

Steps (≈1–2 hours, most of it icon assets):

  1. Confirm the support matrix first. Check your Filament major against the package's composer constraint and its CI/test matrix. A green build on Filament v3 is not evidence for v4. If your panel runs a major that is only "permitted" by the constraint but untested, stop here and prototype in-house instead.
  2. Install and pin.
    composer require jeffersongoncalves/filament-pwa
    
    Commit composer.lock. This pulls laravel-pwa-favicon → jeffersongoncalves/laravel-favicon; you own all three.
  3. Register on exactly one panel (staging, not production):
    // app/Providers/Filament/AdminPanelProvider.php
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->default()
            ->id('admin')
            ->path('admin')
            // ...
            ->plugin(\JeffersonGoncalves\FilamentPwa\FilamentPwaPlugin::make());
    }
    
    Verify the exact plugin FQCN and any make()/->config() signature from the package README before committing — the docs here describe behavior, not the class name.
  4. Publish the config and edit the copy in config/, never vendor:
    php artisan vendor:publish --provider="JeffersonGoncalves\FilamentPwa\FilamentPwaServiceProvider"
    
    Set app name, short name, theme color, display mode, start URL, and icon paths in that file.
  5. Produce the icon set (this is the real work): 192×192 and 512×512 (purpose: "any maskable"), an opaque 180×180 apple-touch-icon, and a favicon .ico/.svg. Put them under public/ with a versioned path (e.g. public/pwa/v1/icon-192.png) so you can cache-bust later.
  6. Verify with an HTTP assertion, not a browser glance:
    curl -s https://staging.example.com/admin | grep -Ei 'manifest|theme-color|apple-touch-icon|msapplication'
    
    Then open DevTools → Application → Manifest to confirm it parses and icons resolve (no 404s, no MIME errors).

First use case to target: a named mobile/on-call user group installing the panel to a home screen with correct branding. Do not announce "offline admin" — you cannot deliver it.

Implementation Patterns

Config-first, code-second. The published config is the primary integration surface. Treat it as application config (checked into git, environment-aware via env()), and keep the plugin registration itself trivial. Resist the urge to subclass the plugin; there is essentially no seam worth extending.

Panel-by-panel rollout. Register the plugin in AdminPanelProvider only. Leave other panels (/ops, /billing, tenant panels) untouched until the first one has been verified in production for a full release cycle. Because there is no migration or data model, rollback is literally deleting one line — preserve that property by not wiring the plugin into a shared base provider.

Test the head, not the browser. Add an HTTP-level test so regressions surface in CI:

it('emits pwa head tags on the admin panel', function () {
    $this->actingAs(User::factory()->create())
        ->get('/admin')
        ->assertOk()
        ->assertSee('rel="manifest"', false)
        ->assertSee('name="theme-color"', false)
        ->assertSee('rel="apple-touch-icon"', false);
});

This catches the failure modes that a visual glance misses: tags silently dropped, another plugin overwriting them, or the panel layout changing in a Filament upgrade.

Use render hooks as your override layer. Filament's PanelsRenderHook::HEAD_END is the same seam the package uses. When you need something the config doesn't expose — a dark-mode theme-color variant, a per-route noindex meta, a manifest link for a second domain — append it yourself rather than forking:

use Filament\Support\Facades\FilamentView;
use Filament\View\PanelsRenderHook;

FilamentView::registerRenderHook(
    PanelsRenderHook::HEAD_END,
    fn () => view('partials.pwa-extra'),
);

Keep the build-vs-buy prototype in your back pocket. The parity implementation is roughly thirty lines: a Blade partial holding the manifest link, theme-color metas, and icon tags, registered on HEAD_END. Run that prototype in parallel during your spike. If it reaches parity within a day — including per-panel config and dark mode — ship the prototype and drop the three-package chain. This is the single highest-leverage decision in the whole evaluation, and it is cheap to make correctly.

Cache-bust the manifest and icons deliberately. Put version numbers in the icon filenames and the manifest URL (/pwa/v1/manifest.webmanifest) rather than relying on query strings — some platforms and CDNs handle query-string caching inconsistently for manifests. If you're behind a CDN that caches HTML, remember the <head> is now part of the cached payload; you'll need a purge on release.

Gotchas and Tips

The name oversells the delivery. "PWA" here means installable-with-branding, not offline. Chrome's install criteria have loosened over time, but install UI still varies by browser and platform, and iOS Safari is its own ecosystem. Test the actual install flow on Android Chrome and iOS Safari; don't infer from DevTools' green checkmarks. Write the boundary into the ticket title so it isn't later read as an incomplete delivery.

A service worker caching authenticated Filament HTML would be a security hazard. Stale authenticated pages served from cache after logout is a real data-exposure path. The absence of one is defensible — state it as a deliberate decision in your architecture notes, or someone will "helpfully" add one later.

CSP failures are invisible. A strict Content-Security-Policy on the admin panel will block the manifest fetch via manifest-src (or default-src fallback) and the icons via img-src — and it fails as console-only errors with no visible breakage. Add manifest-src 'self' and ensure img-src allows your icon path. Check the console on every panel you roll out to.

Check for duplicate injectors. If another plugin, your theme, or a legacy Blade layout already emits theme-color or a manifest link, you get last-writer-wins output that is confusing and hard to diagnose — browsers pick one, usually not the one you expect. Grep your rendered HTML for duplicates before assuming the package misconfigured itself.

Head coverage is panel-layout-scoped. Render hooks fire inside the panel layout. Custom login pages, Filament's error pages, and any non-panel routes will not receive the tags. If your login page is a branded custom view, add the tags there by hand.

Multi-tenant and multi-domain branding is not solved. A manifest is a static file at a static URL. If different tenants or different panel domains need different names/icons, a single package config won't get you there — you need a dynamic manifest route keyed per tenant, served with the correct Content-Type: application/manifest+json, and that is outside this package's surface. The worst failure mode here is one tenant seeing another tenant's icon: verify isolation explicitly if you attempt it.

Dark mode theme-color is worth auditing. Filament's dark theme needs a media="(prefers-color-scheme: dark)" variant of the theme-color meta to avoid a jarring light-colored browser bar in dark mode. Inspect the emitted markup; if it's missing, add it via HEAD_END rather than waiting on upstream.

Transitive dependency lag is your problem, not theirs. Release 2.1.0 was a pure dependency bump — meaning a breaking change in laravel-favicon blocks this package until the single maintainer catches up. You cannot patch that locally without forking. Pin exact versions of all three packages, commit the lockfile, and pre-agree the exit: inline the tag block in a render hook and delete the plugin registration. Nobody has to approve a rollback plan that's already written.

iOS icon specifics. apple-touch-icon must be opaque — iOS composites transparency onto black, which looks broken. Supply 180×180 at minimum. Also confirm your start_url and scope in the manifest actually resolve behind your panel path (/admin), or the installed app opens to a 404.

Debug checklist when it "doesn't work":

  • php artisan config:clear after editing the published config — and remember config:cache in production will freeze it.
  • curl the panel and confirm the tags are in the raw HTML before blaming the browser or the service worker you don't have.
  • DevTools → Application → Manifest: does it parse? Do icons return 200 with the right MIME type?
  • DevTools → Console: CSP violations appear here and nowhere else.
  • Confirm HTTPS — install prompts require a secure origin, and localhost is the only exception.
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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator