Show HN: Drop-In PWA Support for Next.js with next-PWA-pack
by devfamdk | 2025-07-16 14:02:29+00:00
Let’s be honest: building a real PWA in Next.js still sucks.

But when you actually need solid offline support, proper cache control, and version updates that don’t leave users stuck with stale data — suddenly you're deep in custom service worker hell, fighting App Router quirks and rewriting logic for the hundredth time.

So we open-sourced next-pwa-pack, a package that wraps all the annoying parts of adding PWA functionality to a Next.js project into something that's finally tolerable.

We do a lot of client work with Next.js, and “can you make it work offline?” comes up more often than you'd think. Every time, it was a mess — building service workers from scratch, wiring up caching strategies, dealing with versioning bugs across tabs, users getting stale content after deployments, etc.

Existing libraries either didn’t support the App Router well or came with so much config overhead that you might as well write your own service worker. So, we did — once — and turned it into a package. next-pwa-pack wires up a service worker with sane defaults: it caches HTML and static assets, supports offline fallback, and comes with a messaging system to control the cache from your app. No extra config required.

It also handles cache invalidation across browser tabs. That one was tricky — we ended up using localStorage + storage events to broadcast update signals, which has been working well for SPAs. To use it, you just wrap your app in the provided PWAProvider. It handles service worker registration, sets up messaging, and optionally gives you a dev panel during local development to observe or trigger cache behavior manually.

import { PWAProvider } from "next-pwa-pack";

export default function layout({ children }) { return <PWAProvider>{children}</PWAProvider>; }

And that’s pretty much it. The service worker kicks in, and you’re immediately getting basic offline support, background caching, and version-aware updates.

When the package is installed, it copies over a default sw.js, manifest.json, and offline.html into your public/ directory (if they don’t exist). These are all editable, of course — but they give you a working baseline.

The service worker handles two types of caching: HTML pages, with a default TTL of 10 minutes (configurable in sw.js).

There's a built-in messaging protocol using postMessage() so the client can ask the SW to update or clear specific caches. You can call things like updateSWCache(['/dashboard']) after a mutation or form submission, or use clearAllCache() after logout.

We also expose a hook (usePWAStatus) for checking things like whether a new version is available, if the service worker is installed, etc. Useful for adding those “update available” banners you see in real-world PWAs.

During dev, enabling devMode in the provider shows a small status widget in the corner that tells you whether the app is online, whether cache is being used, and gives you buttons to clear or disable it. It’s surprisingly helpful.

It only works over HTTPS (as required by the PWA spec), and it only caches GET requests — no API posts or sensitive data. TTLs and exclusions have to be edited in the service worker for now, though we plan to make that configurable via external JSON in the next version.

Also worth noting: if you’re using Incremental Static Regeneration (ISR), this doesn’t yet have direct support — though we’re exploring integration paths there too.

We built this to scratch our own itch, but it’s working well enough that we figured it might be useful to others. No weird magic. Just a working service worker with a nice DX.

Code: https://github.com/dev-family/next-pwa-pack Feedback welcome, and happy to answer questions or hear horror stories about your own PWA setup!