Add this to your root +layout.svelte in your already working PWA
<script>
import { beforeNavigate } from '$app/navigation';
import { updated } from '$app/stores';
import { onMount } from 'svelte';
// Check for updates and fallback to full page navigation to refresh the app
beforeNavigate(async ({ willUnload, to }) => {
if ($updated && !willUnload && to?.url) {
location.href = to.url.href;
}
});
async function detectSWUpdate() {
const registration = await navigator.serviceWorker.ready;
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
if (!newWorker) return;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
console.log('New update available');
if (confirm('Update available! \nRefresh to update?')) {
newWorker.postMessage({ type: 'skipWaiting' });
window.location.reload();
}
} else {
console.log('Content is now available offline!');
}
}
});
});
}
/**
* @type {Event | undefined}
*/
let deferredInstallEvent;
onMount(() => {
detectSWUpdate
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredInstallEvent = e;
});
});
async function handleInstall() {
// @ts-ignore
deferredInstallEvent?.prompt();
// @ts-ignore
const { choice } = await deferredInstallEvent?.userChoice;
if (choice.outcome === 'accepted') {
// User accepted to install the application
} else {
// User dismissed the prompt
}
deferredInstallEvent = undefined;
}
</script>
{#if deferredInstallEvent}
<button
class="install-button"
on:click={handleInstall}>Install App</button
>
{/if}