Transforming static and dynamic websites into high-performance, installable applications that function seamlessly without an internet connection.
Next.js 16 enables Turbopack by default. However, most PWA plugins (like @ducanh2912/next-pwa) currently rely on Webpack internal hooks. To ensure your Service Worker is generated correctly, you must explicitly force a Webpack build.
1. Update package.json scripts
"scripts": {
"dev": "next dev --webpack",
"build": "next build --webpack",
"start": "next start --webpack"
}2. Silence Turbopack warnings
const nextConfig: NextConfig = {
output: "export",
turbopack: {},
};Loads instantly and never shows the 'downasaur', even in uncertain network conditions.
Responds quickly to user interactions with silky smooth animations and no janky scrolling.
Feels like a natural app on the device, with an immersive user experience.
The magic behind a PWA lies in two key components working together:Service Workers and the Web App Manifest.
Install the modern PWA plugin for Next.js.
npm install @ducanh2912/next-pwa --legacy-peer-depsWrap your configuration with the PWA initializer.
import withPWAInit from "@ducanh2912/next-pwa";
const withPWA = withPWAInit({
dest: "public",
disable: process.env.NODE_ENV === "development",
register: true,
cacheOnFrontEndNav: true,
aggressiveFrontEndNavCaching: true,
});
export default withPWA(nextConfig);Define your app's identity and icons in public/manifest.json.
{
"name": "Engineering Notes",
"short_name": "EngNotes",
"start_url": "/",
"display": "standalone",
"background_color": "#09090b",
"theme_color": "#09090b",
"icons": [
{ "src": "/icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" }
]
}Link your manifest and define the theme color in your root layout.
export const metadata: Metadata = {
title: "Engineering Notes",
manifest: "/manifest.json",
themeColor: "#09090b",
appleWebApp: { capable: true },
};Always use the webpack flag in scripts to avoid Turbopack conflicts.
npm run build --webpackFiles like HTML, CSS, and JS chunks are cached during the Installationphase of the Service Worker. These are assets that don't change until a new version of the app is deployed.
API responses and dynamic images are cached during the Fetch phase. This allows data fetched from a backend to be available even if the user goes offline later.
A caching strategy where the app serves cached content immediately (stale) while fetching fresh content from the network in the background to update the cache.
Registration -> Installation -> Activation. During activation, old caches are typically purged.
It produces a predictable, strictly static set of assets that can be entirely cached for offline-only environments without complex dynamic routing failures.
Cache API is designed for network request/response objects and handles larger data (like images/JS), whereas LocalStorage is for small key-value strings.