Starter Shape

The smallest maintainable PWA is a normal web app with a manifest, installable icons, a tiny service worker, and a clear offline promise.

Default Starter

public/
  manifest.webmanifest
  icons/
    icon-192.png
    icon-512.png
    icon-maskable-512.png
  offline.html
src/
  main.ts
  sw.ts
  storage.ts
  sync-queue.ts
index.html

Minimum Manifest

{
  "id": "/",
  "name": "Ledger PWA",
  "short_name": "Ledger",
  "description": "Offline-friendly ledger for small teams",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#0f172a",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-maskable-512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ]
}

Minimum Service Worker Promise

Start with this promise:

  • The app opens after it has loaded once.
  • Navigation falls back to offline.html when the network is gone.
  • Static assets use versioned cache cleanup.
  • User data lives in IndexedDB, not in service worker globals.
  • Mutations are queued explicitly and replayed in foreground if Background Sync is unavailable.

Do not start with a complicated cache strategy matrix. Add runtime API caching only after you know which responses are safe to reuse.

Exclude From V1

  • Store wrappers.
  • Periodic Background Sync.
  • Push notifications unless notifications are core to the product.
  • Custom native bridges.
  • Offline conflict resolution beyond a clear manual retry/export path.