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.
Scope of change under review: foxws/laravel-pwa 2.6.0 — a single additive change (a Laravel Boost skill/tip, PR #22). No runtime code path is implied, semver is respected, and regression risk from the delta is effectively zero. The evaluation below therefore covers adopting the package as a whole; 2.6.0 is noted only where the Boost skill changes how our team would work.
Product value. This package sells a bounded, well-understood outcome: make a Laravel app installable and give it a survivable offline fallback without standing up a Workbox build pipeline. The relevant scope for a TPM is deliberately narrow — manifest generation, service worker registration, Blade head/body directives, and a generated sw.js with a documented caching posture. It is not an offline-data platform, not a sync engine, and not a replacement for a real frontend build. Framing it that way keeps expectations and the business case honest.
Architecture fit.
X-Inertia, X-Livewire, X-Livewire-Navigate and HX-Request requests, which is the single most important correctness detail for avoiding the classic stale-shell failure.Integration feasibility. Low ceremony and no infrastructure dependency: composer require, vendor:publish --tag="pwa-config", @pwaHead in <head>, @pwaSw before </body>, then php artisan pwa:generate. Cost is dominated by things that are not PHP: producing icon assets (192/512 PNG + apple-touch-icon), running storage:link, authoring an offline.html (the package does not ship the offline page in the generated flow — it must be created), and adding pwa:generate to the deploy pipeline. Those are the line items that turn a one-hour install into a one-week rollout if left unplanned.
Technical risk. Concentrated in browser-contract and release-process concerns, not in the package's PHP:
| Risk | Why it matters to the product | Severity |
|---|---|---|
| Cache invalidation | Package mitigates by appending ?v=<last-modified> to manifest and SW URLs, so each pwa:generate busts stale fetches. This is real mitigation, but it depends entirely on the command actually running on every deploy — skip it and clients are stranded. |
High |
| Generated-artifact clobbering | pwa:generate writes public/manifest.json and copies the SW stub to public/sw.js. Docs elsewhere imply a published view as the customization surface. If we hand-edit public/sw.js, a later regenerate silently overwrites it. This is the top item to verify before build starts. |
High |
| Scope collision | One service worker per scope. Any pre-existing SW (admin panel, CMS, prior experiment) at or above the registration path conflicts. | Medium |
| Security of cached responses | Header-based bypass (X-Inertia/X-Livewire/HX-Request) plus pwa.ignore_paths covers the common cases, but state-changing GETs and non-fetch XHR not under those prefixes need explicit audit. |
Medium |
| Rollback latency | The kill switch is genuinely good — PWA_ENABLED=false + pwa:generate writes a self-unregistering SW that clears caches. But propagation requires the browser to re-check sw.js; with update_interval defaulting to 24h, "off" is not instantaneous. |
Medium |
| Platform variance | Chromium install prompt vs. iOS "Add to Home Screen" manual path. Onboarding must not gate on beforeinstallprompt, which iOS never fires. |
Medium |
| Vendor concentration | 1 star, small/single-maintainer, and a future-dated 2026-10-14 release that warrants a sanity check on provenance. Acceptable for a bounded, replaceable, MIT-licensed concern; unacceptable as a pinned-to-dev-main critical-path dependency. |
Medium |
| Config/route caching | config:cache and route:cache in deploy scripts can freeze or serve stale PWA config/endpoints. Cache clears must be sequenced relative to pwa:generate. |
Low–Medium |
Key questions to close before committing engineering time.
pwa:generate copies into public/sw.js? If the latter, how do we persist custom logic across deploys without patching the package?pwa:generate with config:cache — does it read the live config or the cached config at build time?scope and start_url derived, and can they be overridden cleanly?push handler, and a subscribe flow become in-scope and roughly double the work.) If no, ensure the flag stays off.APP_URL + TrustProxies correctness), which is a silent installability failure?Stack fit. Target the marketing/marketing-adjacent authenticated shell first: static marketing routes plus a static offline page. Do not attempt offline-capable application data in phase one — the package provides a static fallback, not a write-through data layer, and conflating the two is how these projects turn into multi-month efforts.
Migration path — additive and reversible by design.
@pwaHead/@pwaSw into the single base layout, run pwa:generate, validate with DevTools → Application and Lighthouse's installability audit.PWA_ENABLED=false self-unregister path at least once before launch so the rollback is proven, not theorized.Compatibility checks, in cost order.
http://192.168.x.x will not reproduce production behavior and will burn a QA cycle on a non-bug.storage:link presence and icon assets in the built output — verified against npm run build, not just artisan serve.Sequencing and rough sizing.
| # | Step | Owner | Est. |
|---|---|---|---|
| 1 | Version/CI matrix + existing-SW audit | Eng | 0.5 day |
| 2 | Spike: directives, manifest, SW, Lighthouse baseline | Eng | 1–2 days |
| 3 | Icon set (192/512 + apple-touch-icon) and offline.html |
Design + Eng | 1 day |
| 4 | Deploy pipeline: pwa:generate placement vs. config:cache/storage:link |
Platform | 0.5 day |
| 5 | Cache-invalidation & SW-versioning policy signed off with release owner | Eng + TPM | 0.5 day |
| 6 | Cross-device QA: Chromium desktop/Android, iOS Safari, one offline scenario, one stale-deploy scenario | QA | 1–2 days |
| 7 | Support runbook + first-line enablement | TPM + Support | 1 day |
| 8 | Production canary on a low-traffic route, then widen | Eng | 0.5 day |
| 9 | Adopt the 2.6.0 Boost skill into repo agent config | Eng | 0.5 day |
Total: ~5–8 dev-days to production-ready on a single layout, excluding push notifications. Step 9 is strictly last — it is a documentation artifact and must never gate the rollout.
Explicit non-goals for phase one: push notifications, background sync, offline mutation queues, and per-route caching strategies beyond the package defaults. Revisit only after the install flow and stale-deploy behavior have been observed in the wild for a full release cycle.
Maintenance. Small surface area — config, generated static assets, one Blade include, one artisan command in the deploy script. No schema, no queue, no external service, no secrets (unless push is enabled). Ongoing cost is two things: tracking a low-bus-factor dependency (mitigate by pinning an exact version, reviewing changelogs on bump, and confirming the codebase stays small enough to fork or vendor under MIT) and owning the cache-invalidation policy, which is ours regardless of what the package does. The 2.6.0 change adds no maintenance obligation and no attack surface.
Support. Developers consume it; end users experience the failures. Expect two ticket archetypes: "the site is showing old content" and "the app won't update / won't install." Both are near-undiagnosable without a prepared runbook. The runbook must contain, in this order: config flags → DevTools Application tab (is the SW active?) → Network tab (is the request reaching the network?) → Lighthouse. It must also include the remote kill switch (PWA_ENABLED=false + pwa:generate) with a documented expectation that propagation is not immediate, and end-user "clear site data" instructions for the iOS manual-install path. Budget first-line support enablement, not just engineering time — this is the most commonly under-resourced line item in PWA rollouts.
Scaling. No server-side scaling cost and no new runtime to operate. Static assets and the offline fallback move off the origin for repeat visits, reducing bandwidth and improving perceived performance; that is a real but modest win and should not be the primary justification. The meaningful "scaling" cost is client-state complexity: every added caching strategy or offline behavior multiplies the space of states a bug report can come from. Enforce a rule that new caching rules require a named owner and a test scenario in the QA checklist.
Failure modes and pre-agreed responses.
pwa:generate ran, then advise the kill switch if unresolved.storage:link and APP_URL/TrustProxies behind the proxy.pwa:generate. Detection: assert generated artifact freshness in the pipeline. Response: treat as a failed deploy, not a follow-up ticket.update_interval as the worst-case propagation window so nobody promises an instant revert.Ramp-up. Low for any Laravel developer — the whole integration is two Blade directives plus an artisan command. The learning curve is in the browser contract (scope, HTTPS, update lifecycle), not the package. Plan a single 60-minute walkthrough for the team that owns releases, and make the Boost skill the default source of truth for AI-assisted changes so generated code follows the package's conventions (change config → pwa:generate) rather than hand-editing public/sw.js.
Recommendation. Proceed, scoped to installability plus a static offline fallback on the marketing routes, with the deploy-path gate in step 2 as a hard go/no-go and the rollback path exercised before launch. Hold push notifications and any offline data strategy out of scope. Pin the dependency exactly, keep the integration to a single PR's worth of removal, and treat the Boost skill as an additive documentation artifact adopted at the end.
How can I help you explore Laravel packages today?