Skip to content

fix: keep PWA app shell and its files consistent after a rebuild - #26464

Closed
totally-not-ai[bot] wants to merge 2 commits into
mainfrom
fix/pwa-sw-precached-assets-after-rebuild
Closed

totally-not-ai[bot] wants to merge 2 commits into
mainfrom
fix/pwa-sw-precached-assets-after-rebuild

Conversation

@totally-not-ai

@totally-not-ai totally-not-ai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Review risk: two-way door — only the generated PWA service worker (sw.ts) and the dev server's service worker update change; affects apps using @PWA with offline support, reverting is a one-file change.

Fixes #24800

Summary

After a frontend rebuild, an installed PWA could show a permanent blank page on the next reload. This happened when the service worker answered the reload with its precached app shell from the previous build. In development mode, the worker fetched every /VAADIN/ file network-first, so the old shell's entry script was requested from the server, which no longer had it. The worker then passed the server's 404 through and never checked the precache.

Supersedes #24807, which removed only skipWaiting(). With just that change, the blank page still appeared, and a reload no longer recovered it because the new worker stayed in "waiting".

What changed

  • In development mode (which includes dev-bundle builds, since those are built in development mode), /VAADIN/ files that are in the precache are now served from the precache instead of network-first. The files have a content hash in their name, so this is the same cache-first handling production builds already use. Non-precached /VAADIN/ URLs (e.g. the Vite dev server's modules) are still network-first.
  • Removed self.skipWaiting(). A worker from a new build now waits until the previous build's pages are closed. Activating right away removed the previous build's files from the precache while those pages could still load from it (e.g. lazy route chunks in another open window). clientsClaim() is kept so the first install still controls the page without a reload.
  • With the Vite dev server, editing sw.ts still takes effect in the open page. After the update check, the dev server asks the rebuilt worker to skip waiting through a new Vaadin.ServiceWorker.skipWaiting message.
  • Workbox removes outdated precache entries only when a worker activates. Because activation now waits, every build deployed while the app stays open adds its files to the precache. They are removed once the newest worker activates.
  • Behavioral change: a new service worker version takes over only after all windows of the app are closed, not immediately. Pages loaded from the network still get the new build's index.html and files on reload.

Test summary

  • Reproduced with a real @PWA app in dev-bundle mode, driven by headless WebKit (Playwright) and Chromium: install the worker, rebuild with a frontend change and restart the server, then click in the still-open page.
  • With the worker's app-shell fallback forced: unpatched main gives a 404 on the old indexhtml-*.js and a blank page. With this change, the app loads.
  • Normal online path with this change: clicking after the session expires reloads the page into the new build.
  • Dev-server hot update of sw.ts, in Chromium: the rebuilt worker activates and takes over the open page. Without the message it stays in "waiting".
  • No automated test is added. Reproducing this needs two frontend builds of one app plus forcing the offline branch, which the integration-test setup can't do.

🤖 Generated with Claude Code

In development mode, the service worker sent every /VAADIN/ request
network-first, so a precached app shell from the previous build loaded
its entry script from the server, which no longer had it, and the page
stayed blank. Precached dev bundle files are now served from the
precache.

The worker of a new build also no longer activates while pages of the
previous build are open, as activating removed the previous build's
files from the precache that those pages still load from.

Fixes #24800
Without skipWaiting() in the generated worker, a worker rebuilt after
an edit to sw.ts waited until all pages were closed. The dev server
update now asks the rebuilt worker to activate.
@totally-not-ai

Copy link
Copy Markdown
Contributor Author

Superseded by #24807, which now contains these changes.

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PWA standalone: blank screen after frontend bundle rebuild due to mid-load service worker takeover causing 404

0 participants