Next.js 16 ships Turbopack as the default bundler, but
@ducanh2912/next-pwa is webpack-only — the previous PWA setup
required `--webpack` on dev/build/"dev:proxy" scripts, accepting
a perf regression in exchange for working PWA install.
@serwist/turbopack (paired with the official 'serwist' package
and 'esbuild') is the Turbopack-native successor: it works with
the default bundler, no opt-out flag, and gives us a real TS
service worker source file we can read and edit.
What this commit does:
1. Swap deps in package.json:
- Remove @ducanh2912/next-pwa
- Add @serwist/turbopack, serwist, esbuild (all devDeps)
- Drop --webpack flag from dev / dev:proxy / build scripts
(Turbopack is restored as the bundler)
2. next.config.ts:
- Replace withPWAInit wrapping with withSerwist named import
- Update comment block to describe new architecture
3. New src/app/sw.ts: Serwist service worker source.
skipWaiting + clientsClaim + navigationPreload + defaultCache.
This file is compiled to a real SW at build time by Serwist.
4. New src/app/serwist/[[...slug]]/route.ts: catch-all Route
Handler that serves the compiled SW at /serwist/sw.js plus
Serwist's chunked runtime assets. Catch-all is required
because Serwist serves multiple files under /serwist/.
5. src/app/layout.tsx: replace <SwRegister /> with
<SerwistProvider swUrl="/serwist/sw.js"> from
@serwist/turbopack/react. The provider handles registration
+ lifecycle internally.
6. Delete src/app/_components/core/sw-register.tsx: replaced
by SerwistProvider.
7. .gitignore: add public/sw.js and public/workbox-*.js
(defensive — these are build artifacts that change hash
every build and shouldn't be committed).
8. Untrack public/workbox-3c9d0171.js: stale workbox runtime
from the previous next-pwa build, hash is build-specific
so it can never be regenerated correctly.
Not modified (intentionally):
- src/utils/pwa.ts: pure beforeinstallprompt browser-API wrapper,
library-agnostic, stays as-is.
- pwa-install-overlay / pwa-install-dialog: install UI flow
unchanged.
- public/manifest.json: still works; layout's metadata.manifest
points at it.
Verification:
- pnpm install resolved cleanly (added 24 pkgs, removed 209)
- npx tsc --noEmit passes (EXIT=0)
- Serwist provides named exports, not default — withSerwist and
createSerwistRoute are both named imports
PWA install flow was wired but unusable: pwaUtil.install() calls
deferred.prompt(), but Chrome only fires beforeinstallprompt when a
valid Service Worker is registered + a manifest is linked. The project
had both intent (manifest, usePwaInstall hook) but no live SW —
public/sw.js was a stale workbox build artifact from a previous
attempt with hardcoded chunk URLs that no longer matched any build.
What this commit does:
1. Add @ducanh2912/next-pwa@10.2.9 as devDependency.
2. Wrap nextConfig with withPWA({...}):
- dest: "public" SW outputs to public/sw.js
- disable: true skip SW generation in dev (HMR-friendly)
- register: false don't auto-inject registration
- workboxOptions.skipWaiting: true + clientsClaim: true — new SW
takes over immediately
3. Add --webpack flag to dev/build/dev:proxy scripts.
@ducanh2912/next-pwa is webpack-only; Turbopack (Next 16 default)
doesn't load webpack plugins. Per user decision to accept the perf
trade-off in exchange for a working PWA.
4. Delete stale public/sw.js (workbox will regenerate it at build
time with the correct chunk URLs).
5. Add src/app/_components/core/sw-register.tsx: a no-UI client
component that registers /sw.js in production. Mounted at the end
of <body> in layout.tsx. disable: true means dev mode skips
registration (no /sw.js to fetch anyway).
After this commit + pnpm build, public/sw.js is regenerated with
correct chunk URLs, registered on first prod load, and Chrome fires
beforeinstallprompt — usePwaInstall + pwaUtil.install() then trigger
the native install prompt end-to-end.
- Added ic_voicemessage_lock.png for locking voice messages.
- Added pic_bg_vip.png as a background for VIP sections.
- Added pic_popup_voicemessage.png for the voice message popup interface.
Move environment-specific icons into dedicated directories (icons/develop, icons/test, icons/production) and update release scripts to copy the appropriate icon set (favicon.ico, Icon-192.png, Icon-512.png) into public/ for each target environment. Update manifest.json to reference the new /images/icons/ path.
- Splash Skip button now dispatches `AuthGuestLoginSubmitted` instead of
direct routing, keeping guest auth under the state machine
- Update PWA install dialog copy ("Add to Home Screen") and drop favicon
entry from manifest icons
- Add debug logging and routing sequence docs to splash-button
Configure Progressive Web App capabilities for production builds, enabling offline support and installability. PWA is disabled in dev to avoid HMR conflicts, and switches the build to webpack since PWA tooling requires it.