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

foxws/laravel-pwa

Laravel PWA adds progressive web app support to Laravel apps with manifest and service worker generation, install prompts, icons, and offline caching so users can install your site and use it like a native app.

View on GitHub
Deep Wiki
Context7

Getting Started

foxws/laravel-pwa is a generation-based package, not a runtime one: you configure PHP, run an Artisan command that emits public/manifest.json and public/sw.js, and inject two Blade directives into your root layout. Almost every "it doesn't work" moment traces back to skipping the generate step after a config change.

Minimal path:

composer require foxws/laravel-pwa
php artisan vendor:publish --tag="pwa-config"
php artisan storage:link            # default icons expect storage URLs
php artisan pwa:generate            # writes public/manifest.json + public/sw.js

Then in your root layout (one layout only):

<head>
    @pwaHead
</head>
<body>
    ...
    @pwaSw
</body>

Sanity check that the artifacts exist and are versioned:

ls -l public/manifest.json public/sw.js
# both URLs get a ?v= suffix derived from file mtime

Where to look first:

  1. config/pwa.php — the manifest array, icons list, ignore_paths, update_interval, and the enabled flag. This is the source of truth; the files in public/ are generated output.
  2. docs/usage.md and docs/configuration.md — short, and more accurate than reading the package source.
  3. The generated public/sw.js — it's a readable stub, and it's what actually governs caching. Read it once so you know what the defaults do before you customize anything.

First real use case: put @pwaHead/@pwaSw in the layout, add 192×192 and 512×512 PNGs plus an apple-touch-icon where config('pwa.icons') expects them, run pwa:generate, and stop. Verify in Chrome DevTools → Application → Manifest that installability is green and the icons render. Don't touch cache strategy or ignore_paths until the install flow works on a real device — that ordering saves you from debugging two layers at once.

Implementation Patterns

Configure through env, generate on deploy. The manifest fields are .env-driven (PWA_SHORT_NAME, PWA_DESCRIPTION, PWA_THEME_COLOR, PWA_BACKGROUND_COLOR, PWA_DISPLAY, PWA_START_URL), with name coming from APP_NAME. So the natural workflow is: per-environment .env, then php artisan pwa:generate as a deploy step, not a commit-the-artifacts step. That keeps staging and production manifests honest.

Pass through arbitrary manifest keys. Any key added to the manifest array is emitted verbatim, and a key set to null is omitted. That's the extension point for shortcuts, screenshots, categories, id, orientation, display_override:

'manifest' => [
    'shortcuts' => [
        ['name' => 'Dashboard', 'url' => '/dashboard'],
    ],
    'screenshots' => null, // omit entirely
],

Icons: disk vs. literal path. Each icon entry is disk, path, sizes, type. With disk set, the URL is resolved via Storage::disk()->url() at generation time — meaning the resolved absolute URL is baked into manifest.json. With disk => null, path is used as-is, which is what you want if you serve icons from public/ or a CDN with a relative URL. Decide this early; switching modes later means regenerating and re-verifying on device.

One layout, one registration. Duplicating @pwaSw across multiple layouts is how you get two service workers contending for scope. Use it once in the app shell. Override per-need with the directive arguments rather than editing the published template:

@ pwaSw(['swPath' => '/sw.js', 'scope' => '/', 'debug' => true, 'updateInterval' => 12])
@ pwaHead(['themeColor' => '#ff0000', 'manifest' => '/custom.json', 'icon' => '/icon.png'])

debug => true is the cheap switch to turn on when registration silently no-ops.

Keep ignore_paths current as you add XHR surfaces. The service worker already skips Inertia (X-Inertia), Livewire (X-Livewire, X-Livewire-Navigate) and htmx (HX-Request), plus anything in pwa.ignore_paths. Every new API prefix, autocomplete endpoint, polling route, or JSON fetch you add is a candidate. Make adding to ignore_paths part of the PR checklist when you introduce a new fetch() call, rather than discovering it as a stale-response bug weeks later.

Tune update_interval deliberately. It's in hours, default 24; 0 disables periodic update checks. Shorten it during an active release window if you need clients to pick up changes faster, then put it back.

Ship a kill switch and exercise it. PWA_ENABLED=false plus pwa:generate writes a self-unregistering service worker that clears caches and removes itself — the Blade templates and manifest.json are left unchanged. Put this in your runbook and test it once in staging, because a self-unregistering SW that fails on first use is not a rollback plan.

Own the offline page. public/offline.html is yours to create — the package doesn't ship it (there's an example in the repo). Treat it as a real page: brand it, explain what's unavailable, link back to a safe route.

If you use Laravel Boost (2.6.0). The release adds a Boost skill encoding the package's conventions — generate-don't-hand-edit, directive syntax, cache-busting. It's inert at runtime and purely additive; it just means an AI assistant asked to "make the dashboard offline-capable" produces @pwaHead/@pwaSw and a pwa:generate step instead of an ad-hoc Workbox setup.

Gotchas and Tips

  • @pwaHead and @pwaSw are Blade includes, not components. <x-pwa-head /> will not resolve. This is the single most common first-hour mistake.
  • Regenerating is mandatory. Editing config/pwa.php or .env changes nothing until php artisan pwa:generate runs. Conversely, never hand-edit public/manifest.json or public/sw.js — they are overwritten output.
  • Don't commit a stale generate step. If your deploy script runs config:cache and route:cache but not pwa:generate, production serves whatever was last generated. Order it after config changes, before the app goes live.
  • Version busting relies on file mtime. The ?v= suffix comes from each file's last-modified time, so a pwa:generate run busts caches. But artifact copies that preserve mtimes (rsync -a, some tar/CI artifact flows) can defeat this if you build the files in one stage and copy them in another — generate in the final stage, or verify the suffix actually changed after a deploy.
  • PWA_ENABLED=false is a soft disable. The service worker unregisters and clears caches, but the Blade templates and manifest.json are unchanged. Expect the manifest link to remain in the HTML; don't be surprised by it.
  • storage:link and APP_URL. Default icons live under /storage/images/icons/, so storage:link is required, and with disk set the resolved URL is baked at generation time using the app URL. A wrong APP_URL behind a proxy produces a manifest served from one origin referencing icons on another, and installability fails with no visible error. Configure TrustProxies and regenerate.
  • HTTPS or localhost only. Service workers and install prompts are absent on plain HTTP and on http://192.168.x.x. Testing from a phone against php artisan serve --host=0.0.0.0 will look broken for reasons that aren't the package's fault.
  • Existing service workers collide. One SW per scope. Anything already registered at or above / — a previous package, a CMS, an in-house Workbox build — will conflict. Audit the Application panel before you install.
  • The service worker is itself cached hard. After changing the SW or its config, use "Update on reload" in DevTools or you'll conclude your change did nothing.
  • ignore_paths gaps surface late. The Inertia/Livewire/htmx headers are covered for free; your own fetch() calls and API prefixes are not. Symptom is a stale or blank response in a component that works on hard reload.
  • iOS diverges. @pwaHead handles the apple-touch-icon and theme-color, but there is no beforeinstallprompt on Safari — install UX is a manual "Add to Home Screen" instruction. Never gate onboarding on a prompt iOS won't fire.
  • Debugging order that actually terminates: (1) did pwa:generate run and are the ?v= values fresh, (2) DevTools → Application → is the SW active and is the manifest error-free, (3) Network tab → is the request reaching the server at all, (4) Lighthouse installability. Jumping to cache-rule surgery skips steps that explain most failures.
  • Pin the version and keep the integration thin. Small maintainer footprint; the concern is bounded and replaceable. Keeping customization in config/pwa.php and your own public/offline.html means removal is a one-PR operation, and every future bump is a changelog read rather than a code audit.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager