Work offline
pwa() turns the client build into an installable app that survives a lost connection.
pwa() is opt-in and has two halves that an app not using it never loads. The build half is a Vite plugin: after the client build it writes the web app manifest, an offline page, and sw.js, a service worker generated from the files the build produced and the caching rules you give it. No runtime library is involved. The page half, @shadow-js/dusk/features/pwa, registers the worker and tells the page when a new version is waiting.
import { dusk, pwa } from "@shadow-js/dusk/vite";
export default {
plugins: [
dusk(),
pwa({
manifest: {
name: "Example Shop",
short_name: "Example",
start_url: "/",
display: "standalone",
theme_color: "#1d2b36",
background_color: "#ffffff",
icons: [
{ src: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any maskable" },
],
},
runtime: [
{ pattern: "/api/products/**", strategy: "stale-while-revalidate", maxEntries: 30, maxAgeSeconds: 3600 },
{ pattern: "/images/**", strategy: "cache-first", cacheName: "images", maxEntries: 60 },
{ pattern: "/api/feed", strategy: "network-first", networkTimeoutSeconds: 3 },
],
}),
],
};
Each rule names a path pattern and a strategy: cache-first for files that never change, network-first with a timeout for data that should be fresh when it can be, and stale-while-revalidate for the middle. The plugin does nothing in vite dev.
A generated file never replaces a file of the same name in public/: the build fails and names it. The page that is shown when a navigation fails is offline.html, and some responses are never kept: those marked no-store or private (which is what Dusk sends for a page unless a route sets its own lifetime), those with Vary: Cookie, and those that set a cookie.
This is the end of the short path. The Dusk overview maps every guide, and the reference lists every export. Installable apps and offline covers updates, the registration helpers and what is never cached.