Back to Blog

Effortless PWA Integration in Next.js with next-pwa-pack

Content
At dev.family, we love building tools that make our own lives easier — and then open-sourcing them so the rest of the community can benefit too. One of our latest contributions is next-pwa-pack – a package that helps you integrate full-fledged PWA functionality into your Next.js app in just a few minutes.

Why I Built This Package

Clients occasionally ask us to add PWA features to their apps, and it always turns into a major headache. I tried existing solutions, but they were overly complex and required a lot of manual setup. Others just didn’t work well with the Next.js App Router.
I was constantly writing service workers from scratch, configuring caching strategies, handling updates, and trying to keep everything in sync across multiple tabs. It took too much time just to reliably implement basic PWA behavior. Even then, there were issues. After every deployment, users were served outdated versions unless they manually cleared their cache, but most didn't. Some browsers behaved differently, which meant adding more logic to handle edge cases.
What I really needed was a universal, drop-in solution that just worked without forcing developers to become experts in service worker APIs.

Building the Package

I began by identifying all the pain points I had encountered. My goal was clear: to enable developers to provide robust PWA support in their app with a single line of code.
First, I created a base service worker that caches HTML pages with a configurable TTL, as well as handling static assets and offline mode. Then I added a messaging system that allows the client to communicate with the service worker to manage cache updates, for example. Next, I wrote scripts that automatically copy the necessary files (sw.js, manifest.json, and offline.html) into the project during installation.
One tricky part was keeping the cache in sync across tabs, which is crucial for single-page applications (SPAs) where users might have multiple tabs open. I solved this issue by using localStorage and storage events for lightweight tab-to-tab messaging.
Finally, I built a package that works right out of the box without requiring complex configuration. Just plug it in and focus on building your app.
Looking for a development partner? Tell us about your project!

Why Use next-pwa-pack

Installing next-pwa-pack solves a bunch of common headaches that come with adding PWA support to a Next.js app:
  • Automatic service worker registration – no need to manually write boilerplate code for registering or managing service workers;
  • Cache management – comes with simple APIs to clear, update, or disable cache when needed;
  • Cross-tab synchronization – automatically keeps cache up to date across all open tabs;
  • Offline support – your app continues to work even when the user loses internet connectivity;
  • Developer tools built-in – includes a dev panel to help you monitor and control PWA behavior during development.
You can download next-pwa-pack here: https://github.com/dev-family/next-pwa-pack

What Happens During Installation

When you install the package the following essential files are automatically copied into your project’s public folder:
  • sw.js – a ready-to-go service worker with built-in caching logic;
  • offline.html – a fallback page shown when the app is offline;
  • manifest.json – a basic PWA manifest file (you’ll need to customize it for your app).
❗ If any of these files already exist in your public directory, they won’t be overwritten.
If you need to copy the files manually, you can run:
node node_modules/next-pwa-pack/scripts/copy-pwa-files.mjs
# or
npx next-pwa-pack/scripts/copy-pwa-files.mjs

Configuring manifest.json

After installation, make sure to update public/manifest.json to match your project’s settings:
{ "name": "My app", "short_name": "My app", "description": "My app’s description", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#000000", "icons": [ { "src": "/icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" } ]
}
Also, don’t forget to add your own icons to the public/icons/ folder, or adjust the icon paths in manifest.json accordingly.

Quick Start

To get up and running, simply wrap your application in the PWAProvider, component – all the necessary PWA functionality will be hooked up automatically:
import { PWAProvider } from "next-pwa-pack";
export default function layout({ children }) { return <PWAProvider>{children}</PWAProvider>;
}
That’s it! The package takes care of the rest.
Want to develop a PWA application? Book free consultation now!
Max Bantsevich
CEO dev.family

What’s Inside PWAProvider

Let’s break down the key components.

RegisterSW

This component handles service worker registration automatically. It checks if the browser supports service workers, and if it does, register sw.js by default. If something goes wrong during registration, it logs an error to the console.

CacheCurrentPage

Responsible for caching the current page. It intercepts navigation events (including SPA-style route changes) and sends the HTML content to the service worker for caching. This enables proper offline support for dynamic pages.

SWRevalidateListener

Listens for localStorage changes and triggers cache updates for specified URLs. This ensures all tabs stay in sync – a must-have for any modern SPA.

DevPWAStatus

Provides a built-in developer panel (enabled via the devMode prop in PWAProvider) that helps you monitor and manage your PWA during development.
import { PWAProvider } from "next-pwa-pack";
export default function layout({ children }) { return <PWAProvider devMode>{children}</PWAProvider>;
}
It displays your app’s online/offline status, shows if a new version is available, and gives you quick access to helpful actions like:
  • Clearing the cache;
  • Reloading the service worker;
  • Refreshing the current page’s cache;
  • Unregistering the service worker and wiping all caches;
  • Enabling or disabling the PWA cache on the fly.

What Does the Service Worker Do?

A service worker is a script that runs in the background, independently of your web page, and helps manage caching, network requests, and updates. In next-paw-pack the service worker handles several key tasks:
HTML Page Caching:
  • Caches HTML pages with a default TTL (time to live) of 10 minutes – you can customize this in /sw.js;
  • Automatically refreshes the cache once the TTL expires;
  • Serves the cached version if the user is offline.
Static Asset Caching:
  • CSS, JavaScript, and images are permanently cached;
  • This improves loading speed on repeated visits;
  • It works only with GET requests for security purposes.
Message Handling:
The service worker listens for five message types from the client:
  • CACHE_CURRENT_HTML – cache the current page;
  • REVALIDATE_URL – force cache update;
  • DISABLE_CACHE / ENABLE_CACHE – toggle caching on or off;
  • SKIP_WAITING – immediately activate a new service worker version.
Offline Mode:
  • Automatically serves offline.html when there’s no network connection;
  • Tries to fetch fresh content as soon as the connection is restored.

Examples of Use

I will also show some real-life scenarios of how the package works.

Cache update after data change

import { updateSWCache } from "next-pwa-pack";
// After the post is successfully created
const handleCreatePost = async (data) => { await createPost(data); // Refresh the blog and dashboard cache updateSWCache(["/blog", "/dashboard"]);
};

Clear the cache when the user logs out

import { clearAllCache } from "next-pwa-pack";
const handleLogout = async () => { await logout(); await clearAllCache(); // Clear all caches router.push("/login");
};

Notification of new version

import { usePWAStatus } from "next-pwa-pack";
function UpdateNotification() { const { hasUpdate, update } = usePWAStatus(); if (hasUpdate) { return ( <div className="update-banner"> <p>New version of the application is available</p> <button onClick={update}>Update</button> </div> ); } return null;
}

Brief description of all exported actions

import { clearAllCache, reloadServiceWorker, updatePageCache, unregisterServiceWorkerAndClearCache, updateSWCache, disablePWACache, enablePWACache, usePWAStatus
} from "next-pwa-pack";
// Clears all caches associated with the service worker.
await clearAllCache();
// Reloads the service worker and refreshes the page.
await reloadServiceWorker();
// Refreshes the cache for the specified page (or the current page if not specified).
await updatePageCache("/about");
// Disables the service worker and clears the cache.
await unregisterServiceWorkerAndClearCache();
// Starts the signal for all tabs and refreshes the cache in the current tab.
// Can be called after revalidateTag on the client.
updateSWCache(["/page1", "/page2"]);
// Globally disables the PWA cache (before rebooting or calling enablePWACache).
disablePWACache();
// Globally enables the PWA cache (after disablePWACache call).
enablePWACache();
const { online, hasUpdate, swInstalled, update } = usePWAStatus();
// - `online` — online/offline status
// - `hasUpdate` — whether update is available
// - `swInstalled` — whether service worker is installed
// - `update()` — activate new application version

Debugging & Monitoring

Verifying Cache Behavior
  1. Open DevTools → Application → Service Workers.
  2. Make sure the service worker is successfully registered.
  3. Go to Cache Storage → html-cache-v2.
  4. Confirm that your pages are being cached properly.
Testing Offline Mode
  1. Enable devMode in PWAProvider.
  2. Open the PWA dev panel (find the green dot in the bottom-left corner).
  3. Turn off the network in DevTools → Network → Offline.
  4. Reload the page – it should display like offline.html.
Console Logs
The service worker logs helpful messages to the console, such as:
  • [PWA] Service Worker registered – successful registration;
  • [SW] Cached: /about – cached page;
  • [SW] Revalidated and updated cache for: /blog – cache updated.

Limitations & Considerations

A few things to keep in mind before installing:
Security
  • HTTPS is required for PWA features to work in production;
  • Only GET requests are cached (API requests are not);
  • Sensitive data should never be stored in cache.
Performance
  • The package has no negative impact on runtime performance;
  • It actually speeds up repeat visits by serving cached pages and assets.
Configuration
  • Cache TTL (default: 10 minutes) can only be changed in sw.js;
  • You can configure cache exclusions via CACHE_EXCLUDE;
  • Manifest.json must be manually customized for your app.

Final Thoughts

next-pwa-pack – is a reliable way to turn your Next.js app into a full-fledged Progressive Web App. It tackles the toughest parts of PWA integration and gives you a clean, developer-friendly API to manage your service worker and caching logic.
Here’s what we’re planning for upcoming versions:
  • TTL configuration via external config (no more editing sw.js);
  • Push notification support to inform the users;
  • More flexible caching patterns based on URL rules;
  • Built-in integration with Next.js ISR (Incremental Static Regeneration);
  • Performance metrics to track caching effectiveness.
That’s it! Thanks for reading 🙂
Still have questions about installing or using the package? Feel free to ask!

You may also like: