Skip to content

PWA service worker doesn't auto-reload open tabs after a new build #9

Description

@ashwin-pc

Problem

The web UI does not update in browsers that have a long-lived open tab, even after dist/ is rebuilt and re-served. This is a service-worker caching issue, not a backend issue.

VitePWA (vite.config.ts) ships a service worker that precaches the app shell:

workbox: {
  navigateFallback: "/index.html",
  globPatterns: ["**/*.{js,css,html,svg,png,woff2}"], // precaches JS/CSS bundles
  ...
}

The SW intercepts requests and serves cached JS/CSS before hitting the server, so rebuilds don't reach those clients.

Why only some browsers

The generated registerSW.js is the minimal stub (from registerType: "autoUpdate"):

if('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js', { scope: '/' })
  })
}

It relies on skipWaiting + clientsClaim but has no reload-on-update logic, and the app never imports virtual:pwa-register. As a result:

  1. The browser only checks for a new sw.js on a full navigation/reload (or ~every 24h). pi-web is a long-lived SPA tab, so tabs that are never reloaded never pick up a new SW.
  2. Even when a new SW activates, the already-loaded page keeps running the old JS until reloaded — there is no controllerchange listener to force a refresh.

Recommended fix

Add auto-reload-on-update so clients get new code without manual intervention. Either:

  • Add a one-time controllerchange listener that reloads:
    let refreshing = false;
    navigator.serviceWorker.addEventListener('controllerchange', () => {
      if (refreshing) return;
      refreshing = true;
      window.location.reload();
    });
  • Or wire up virtual:pwa-register with registerSW({ immediate: true }) so vite-plugin-pwa's full auto-update flow runs instead of the minimal stub.

Workaround (already applied)

Per stuck browser: DevTools → Application → Service Workers → Unregister (or enable "Update on reload"), then hard reload. Or close all tabs of the app and reopen.

Metadata

Metadata

Assignees

No one assigned

    Labels

    implemented-downstreamImplemented in a downstream fork; track when upstream lands the same change

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions