Swoff Swoff

PWA & Installability

Configure your app as a PWA with install prompts.

Prerequisites

  1. HTTPS or localhost — Required for SW and PWA
  2. Service Worker active — Already set up via SW TemplateBuild ScriptsClient Registration
  3. Web App Manifest — Describes your app

Setup with CLI

If you're using the CLI, the PWA install handler is generated automatically when features.pwa is true:

npx @swoff/cli generate
# Generates: swoff/pwa-install.js, public/manifest.json

Configuring Install Behavior

Control whether the browser's native install prompt shows naturally or is suppressed:

swoff.config.json
{
  "features": {
    "pwa": {
      "enabled": true,
      "preventDefaultInstall": false
    }
  }
}
preventDefaultInstallBehavior
false (default)Browser shows native prompt naturally. Event is captured for manual triggering.
trueBrowser prompt is suppressed. Dev must call promptInstall() manually.

Manual Setup

Create Manifest

Create public/manifest.json:

{
  "name": "My Offline App",
  "short_name": "MyApp",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" }
  ]
}
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#000000" />

Handle Install Prompt

window.addEventListener("beforeinstallprompt", (e) => {
  e.preventDefault();
  window.deferredInstallPrompt = e as any;
});

async function promptInstall() {
  await window.deferredInstallPrompt?.prompt();
  const { outcome } = await window.deferredInstallPrompt.userChoice;
}

iOS

iOS Safari does not fire the beforeinstallprompt event. Detect and show manual install instructions:

function detectPWAInstallability() {
  const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent);

  if (isIOS) {
    showIOSInstallInstructions();
    return;
  }

  window.addEventListener("beforeinstallprompt", (e) => {
    e.preventDefault();
    window.deferredInstallPrompt = e;
  });
}

function showIOSInstallInstructions() {
  // Guide users to: Share → Add to Home Screen
  console.log("iOS: Use Share menu → Add to Home Screen");
}

Using the Generated Handler

If you generated swoff/pwa-install.js via the CLI:

import { isInstallable, promptInstall } from './swoff/pwa-install.js';

// Listen for installable event
window.addEventListener('pwa-installable', (e) => {
  console.log('PWA can be installed:', e.detail.isInstallable);
  // Show your custom install button
});

// When user clicks install button
async function onInstallClick() {
  const result = await promptInstall();
  console.log('User choice:', result);
}

Detect PWA Mode

function isRunningAsPWA(): boolean {
  return (
    window.matchMedia("(display-mode: standalone)").matches ||
    (navigator as any).standalone === true
  );
}

Generate Icons

Create icons at 192x192 and 512x512 minimum.

Test PWA

  1. DevTools → Application → Manifest
  2. Verify manifest loads
  3. Check install icon in address bar
  4. Test offline mode

Checklist

  • Manifest created and linked
  • Icons generated (192x192, 512x512)
  • SW registered (see Client Registration)
  • Offline works
  • Install prompt works

Next Steps

On this page