laravel-pwa-service-worker maintained by jeffersongoncalves

Laravel PWA Service Worker
Serve a production-ready PWA service worker at /sw.js, rendered from a Blade template whose cache version tracks the Vite build manifest hash — so a npm run build automatically busts the SW cache without you editing the worker.
Ships three caching strategies out of the box:
- cache-first for content-hashed
/build/*assets (immutable, safe to cache forever) - network-first for HTML navigations (fresh when online, cached + offline fallback when not)
- stale-while-revalidate for everything else (images, fonts, …)
…plus precache on install, old-cache teardown on activate, and a pwa-updated postMessage so your front-end can show an "update available" toast.
Installation
composer require jeffersongoncalves/laravel-pwa-service-worker
The /sw.js route is registered automatically. Register the service worker from your layout:
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
Optionally publish the config and view:
php artisan vendor:publish --tag="pwa-service-worker-config"
php artisan vendor:publish --tag="pwa-service-worker-views"
Update notifications
When a new worker activates it postMessages { type: 'pwa-updated', version } to every controlled page. Listen for it to surface an "update available" prompt and reload once the user accepts:
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
let reloading = false;
navigator.serviceWorker.addEventListener('message', (event) => {
if (event.data?.type !== 'pwa-updated') {
return;
}
// First install fires this too; only prompt when a worker was already
// in control (i.e. this is a genuine upgrade, not the initial load).
if (!navigator.serviceWorker.controller) {
return;
}
if (window.confirm('A new version is available. Reload now?')) {
window.location.reload();
}
});
// Guard against reload loops if the SW takes control mid-session.
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (reloading) {
return;
}
reloading = true;
});
}
</script>
Offline fallback
The worker serves config('pwa-service-worker.offline_url') (default /offline) for a failed navigation when nothing is cached. You register that route — it is intentionally not provided, since the offline page is app-specific:
Route::view('/offline', 'offline')->name('pwa.offline');
Configuration
| Key | Default | Description |
|---|---|---|
enabled |
true |
Register the /sw.js route. |
path |
sw.js |
Route path (keep at root for a / scope). |
middleware |
[] |
Extra middleware applied to the /sw.js route (e.g. a security-headers middleware). |
view |
pwa-service-worker::sw |
Blade view rendered as the worker body. |
build_manifest |
public/build/manifest.json |
md5 seeds the cache version. |
cache_prefix |
pwa |
Cache name is {prefix}-v{version}. |
offline_url |
/offline |
Fallback for failed navigations. |
precache_urls |
['/offline', '/'] |
Seeded on install. |
passthrough_prefixes |
['/admin', '/livewire', '/api', …] |
Always hit the network. |
passthrough_exact |
['/sw.js', '/manifest.json'] |
Always hit the network (exact path). |
asset_prefix |
/build/ |
Content-hashed assets served cache-first. |
Testing
composer test
License
The MIT License (MIT). Please see License File for more information.