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

Laravel Pwa Laravel Package

erag/laravel-pwa

Laravel PWA adds progressive web app support to Laravel 8–13 projects: auto-generated manifest and service worker, a configurable add-to-home-screen prompt, and Blade directives. Works with Blade, Livewire, Vue 3, and React; requires HTTPS.

View on GitHub
Deep Wiki
Context7

Getting Started

Day-one setup (~5 minutes)

composer require erag/laravel-pwa
php artisan erag:install-pwa      # publishes config/pwa.php + manifest/sw/offline assets

Provider registration: Laravel 11/12/13 normally pick it up via package discovery, so treat the bootstrap/providers.php entry as a fallback if the erag:* commands aren't listed by php artisan list. On 8/9/10 you must add EragLaravelPwa\EragLaravelPwaServiceProvider::class to config/app.php.

Then drop the two directives into your main layout:

<head>
    @PwaHead
</head>
<body>
    ...
    @RegisterServiceWorkerScript
</body>

Where to look first

  • config/pwa.php — the only file you should be editing by hand (name, short_name, theme_color, background_color, display, icons, install-button, debug, livewire-app).
  • public/manifest.json, public/sw.js, public/offline.html — generated artifacts. Read them once to understand what the package actually ships, but don't hand-edit: they'll be overwritten.
  • public/logo.png — hard-coded filename, the package will not look anywhere else.

First realistic use case: an internal or customer-facing Blade app where you want an install prompt and an offline fallback without writing a manifest or a service worker by hand. Put the directives in resources/views/layouts/app.blade.php, set your real app name and colors in config/pwa.php, run php artisan erag:update-manifest, then verify:

  1. view-source: shows <link rel="manifest"> and the theme-color meta.
  2. DevTools → Application → Manifest loads with no console errors and icons resolve.
  3. DevTools → Application → Service Workers shows it activated (not "waiting").
  4. Lighthouse PWA audit passes the installability checks.

Test on https:// or localhost only — nothing here works over plain HTTP on a LAN IP, and that includes php artisan serve on 127.0.0.1 from a phone.

Implementation Patterns

Keep the directives in one shared partial. Every layout (app, guest, admin) should @include('partials.pwa') rather than duplicating the two directives. When the package changes directive names or adds a third tag, you edit one file.

{{-- resources/views/partials/pwa.blade.php --}}
@PwaHead
{{-- ... --}}
@RegisterServiceWorkerScript

Inertia / Vue / React / Livewire: the directives only need to live in the root Blade file that bootstraps the SPA — the emitted script talks to the browser directly, so Vue 3 and React get the install prompt for free. For Livewire, set 'livewire-app' => true in config/pwa.php; this exists specifically because Livewire's navigation lifecycle was breaking service worker registration (fixed in v1.8.0, refined again in v2.1.0 for iOS standalone mode). If you use wire:navigate, test that the install button still appears after client-side navigation.

Environment-driven config, not runtime edits. The clean pattern is: per-environment values in config/pwa.php reading env(), then regenerate the manifest as a deploy step.

// config/pwa.php
'install-button' => env('PWA_INSTALL_BUTTON', true),
'debug' => env('PWA_DEBUG', false),
'manifest' => [
    'name' => env('PWA_NAME', config('app.name')),
    'short_name' => env('PWA_SHORT_NAME', 'App'),
    'theme_color' => '#0f172a',
    'background_color' => '#0f172a',
    'display' => 'standalone',
    'icons' => [
        ['src' => 'logo.png', 'sizes' => '512x512', 'type' => 'image/png'],
    ],
],
# deploy script, after `php artisan config:cache`
php artisan erag:update-manifest

This keeps the generated file in sync with the environment and avoids a controller writing to disk on a request path.

Multi-tenant / white-label manifests. The PWA facade writes public/manifest.json immediately:

use EragLaravelPwa\Facades\PWA;

PWA::update([
    'name' => $tenant->name,
    'short_name' => str($tenant->name)->limit(12, ''),
    'theme_color' => $tenant->brand_color,
    'background_color' => $tenant->brand_color,
    'display' => 'standalone',
    'icons' => [['src' => 'logo.png', 'sizes' => '512x512', 'type' => 'image/png']],
]);

Understand what this is: a global file write. It works for single-tenant or "one deployment per tenant" setups. For true multi-tenant on one domain you're racing requests and the last writer wins for everyone — so either run one hostname per tenant, or accept a shared manifest and vary only what you can vary per-request (theme-color meta).

Logo upload flow. Use the built-in handler rather than hand-rolling the file move, since it enforces the constraints the manifest expects:

use EragLaravelPwa\Core\PWA;

public function uploadLogo(Request $request)
{
    $request->validate(['logo' => ['required', 'file', 'mimes:png', 'max:1024']]);

    $response = PWA::processLogo($request);

    return $response['status']
        ? back()->with('success', $response['message'])
        : back()->withErrors($response['errors'] ?? ['Upload failed.']);
}

Guard it behind an admin gate — it overwrites a publicly served asset.

Verification test in CI. Cheap regression net that catches a broken deploy:

it('serves a valid pwa manifest', function () {
    $manifest = json_decode(file_get_contents(public_path('manifest.json')), true);

    expect($manifest)->toHaveKeys(['name', 'short_name', 'icons', 'start_url'])
        ->and(file_exists(public_path('sw.js')))->toBeTrue();
});

Plus a feature test asserting @PwaHead and @RegisterServiceWorkerScript render on the homepage layout.

Gotchas and Tips

  • HTTPS is non-negotiable. Service workers, install prompts, and Lighthouse PWA checks all fail silently over http. Use localhost for dev, a real cert or tunnel (Valet/Herd share, ngrok) for device testing.
  • logo.png is not configurable. The name is fixed and must be at public/logo.png. If your brand asset is icon-512.png, copy it to logo.png; do not try to change the src value without also placing that file in public/, because the manifest src is resolved relative to public/.
  • Manifest edits require a command. Changing config/pwa.php does nothing until php artisan erag:update-manifest. This is the #1 "why is the app name still Laravel PWA?" cause. Wire it into your deploy script so it can never be skipped.
  • public/ must be writable for PWA::update() and processLogo(). On containerized or read-only-root deployments this throws — prefer generating assets at build/deploy time instead of at request time.
  • Multi-server deploys diverge. Writing the manifest on one web node doesn't propagate to the others. Run the generator during deploy, or commit public/manifest.json and public/sw.js to the repo and treat them as build artifacts.
  • Docs namespace mismatch: the facade example imports EragLaravelPwa\Facades\PWA while the logo example imports EragLaravelPwa\Core\PWA. Check the actual class before copy-pasting; if the facade isn't registered, fall back to the core class static call.
  • display: 'fullscreen' is the default and is aggressive. It hides browser chrome entirely, which surprises users and complicates in-app navigation on iOS. Use 'standalone' unless you specifically want kiosk behavior.
  • One 512×512 icon is the minimum, not the target. Add 192x192 and a purpose: "maskable" entry for Android adaptive icons, and ship an apple-touch-icon for iOS — the v2.1.0 release had to patch iOS install/standalone handling, so don't assume Apple is fully covered by the defaults.
  • Stale service worker is the classic post-deploy bug. If users see last week's HTML, the SW cache is serving it. Keep a version marker in the cache name, use skipWaiting/clients.claim deliberately, and always verify after deploy with DevTools → Application → "Update on reload". Never let the SW cache authenticated or CSRF-bearing routes.
  • Set debug => false in production. The config flag drives console.log output in the browser globally — useful while wiring the install button, noisy in prod.
  • Install button visibility: 'install-button' => true is global. If you want it only on marketing pages, toggle it via the env-backed config per environment or gate the directive's inclusion in your partial, rather than shipping it on every authenticated screen.
  • Subdirectory installs: if the app is served from /app rather than the domain root, check the SW scope and start_url in the generated manifest — the default assumes root.
  • Small-package risk. This is a thin convenience wrapper (202 stars, zero dependents) around roughly 50 lines of manifest JSON plus a registration snippet. That's fine — but know the exit path: the generated files in public/ are plain, standards-compliant JSON and JS. If the package stalls or you outgrow it, delete the composer dependency, keep the two files, and inline the four lines of registration JS. Don't build app logic on top of the PWA facade in more than one place.
  • Debugging order of operations when the prompt doesn't appear: is the page HTTPS/localhost → is manifest.json reachable at the right path and valid JSON → is the SW activated with scope / → did the beforeinstallprompt event fire at all (it won't if criteria aren't met, and it never fires on iOS — iOS uses a manual Share → Add to Home Screen flow) → is install-button true.
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.
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk