Activating PWA in Next.js 13+ App Directory Using @Serwist — Simple Guide

Introduction:
PWA or Progressive Web App is a web application that uses modern web capabilities to deliver a more native app-like experience to users. PWAs aim to combine the best of web and native apps.
This guide will show how we can make our Next.js with AppDir a PWA!
I have discussed how to make your Next.js App PWA using @ducanh2912/next-pwa
, but this guide will focus on @serwist
, which is a more recommended way for Next.js. If you want to read about @ducanh2912/next-pwa
you can check out the following article:
Benefits of PWA:
There are a lot of benefits to using PWA and some of them are:
- Offline Access.
- App-Like.
- Push Notifications.
- Discoverable.
- Installable.
- Automatic Updates.
and many more…
Companies Using PWA:
To mention some of the companies that are using PWA:
- AliExpress
- Twitter Lite
- Forbes
and much more… Source
Adding PWA support to Next.js:
Step 1 — Create the Next.js App:
To create a new Next.js Project run the following command:
npx create-next-app@latest
Choose the prompts accordingly:
What is your project named? next-with-pwa
Would you like to use TypeScript? Yes
Would you like to use ESLint? Yes
Would you like to use Tailwind CSS? Yes
Would you like to use `src/` directory? Yes
Would you like to use App Router? (recommended) Yes
Would you like to customize the default import alias (@/*)? No
If you already have a Next project go to the next step.
Step 2 — Add the Required Package:
Now install the following package:
npm i @serwist/next @serwist/precaching @serwist/sw
Step 3 — Configure the Package:
With the latest next.js, we get next.config.mjs
, which would be configured as follows:
// @ts-check
import withSerwistInit from "@serwist/next";
const withSerwist = withSerwistInit({
cacheOnFrontEndNav: true,
swSrc: "src/sw.ts", // add the path where you create sw.ts
swDest: "public/sw.js",
reloadOnOnline: true,
disable: process.env.NODE_ENV === "development", // to disable pwa in development
// ... other options
});
/** @type {import('next').NextConfig} */
const nextConfig = {
swcMinify: true,
reactStrictMode: true,
// ... other next.js config options
};
export default withSerwist(nextConfig);
If you have next.config.js
, the configuration would be as follows:
const withSerwist = require("@serwist/next").default({
cacheOnFrontEndNav: true,
swSrc: "src/sw.ts", // add the path where you create sw.ts
swDest: "public/sw.js",
reloadOnOnline: true,
disable: process.env.NODE_ENV === "development", // to disable pwa in development
// ... other options
});
/** @type {import('next').NextConfig} */
const nextConfig = {
swcMinify: true,
reactStrictMode: true,
// ... other next.js config options
};
export default withSerwist(nextConfig);
If you are using TypeScript, update the tsconfig.json
as follows:
{
// Other stuff...
"compilerOptions": {
// Other options...
"types": [
// Other types...
// This allows Serwist to type `window.serwist`.
"@serwist/next/typings"
],
"lib": [
// Other libs...
// Add this! Doing so adds WebWorker and ServiceWorker types to the global.
"webworker"
],
},
}
For more options, visit the official documentation here:
Step 4 — Add a Service Worker Configuration:
Add the following configuration at src
of your project in a file sw.ts
:
// sw.ts
import { defaultCache } from "@serwist/next/browser";
import type { PrecacheEntry } from "@serwist/precaching";
import { installSerwist } from "@serwist/sw";
declare const self: ServiceWorkerGlobalScope & {
// Change this attribute's name to your `injectionPoint`.
__SW_MANIFEST: (PrecacheEntry | string)[] | undefined;
};
// Anything random.
const revision = crypto.randomUUID();
installSerwist({
precacheEntries: self.__SW_MANIFEST,
skipWaiting: true,
clientsClaim: true,
navigationPreload: true,
runtimeCaching: defaultCache,
fallbacks: {
entries: [
{
url: "/~offline",
revision,
matcher({ request }) {
return request.destination === "document";
},
},
],
},
});
Step 5— Add a Fallback Page to be Shown When User is Offline:
Place the ~offline/page.tsx
in /app
folder and then add the following code to show as a fallback.
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Offline",
};
export default function Page() {
return (
<>
<h1>This is offline fallback page</h1>
<h2>When offline, any page route will fallback to this page</h2>
</>
);
}
Step 6— Add manifest.json to your app:
Go to public
folder of your app and create manifest.json with the following structure:
{
"id": "any_id",
"name": "Next.js PWA - Tutorial",
"short_name": "Next PWA",
"description": "This next.js app is a PWA.",
"icons": [
{
"src": "./logo72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "./logo192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "./logo384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "./logo512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#FFFFFF",
"background_color": "#FFFFFF",
"start_url": "/",
"scope": ".",
"display": "standalone",
"orientation": "portrait"
}
Step 6— Registering manifest.json in Metadata:
Update your layout.tsx/layout.jsx to register the manifest file in the metadata:
import { Metadata } from "next";
// ... other imports
export const metadata: Metadata = {
manifest: "/manifest.json", // we are accessing our manifest file here
title: "...",
// ... other options
};
const RootLayout = ({ children }: { children: React.ReactNode }) => {
return (
<html lang="en">
<body>
<main>{children}</main>
</body>
</html>
);
};
export default RootLayout;
And that’s it, Happy Coding! 😊
Access the Code at:
Visit the Live version at: https://next-pwa-serwist.vercel.app/
If you liked my story you can follow me for more interesting tips and tricks at Farasat Ali — Medium.
You can Find Me on LinkedIn and GitHub.
Also, Visit My Portfolio at:
You can also check out the related articles:
Stackademic
Thank you for reading until the end. Before you go: