Setup guide · Next.js
Turn a Next.js app into an installable PWA (2026)
Add a Web App Manifest, service worker and install prompt to any Next.js 14+ app — App Router and Pages Router, with working code.
Key takeaways
- Ship /manifest.webmanifest from the /app or /public folder — Next.js serves it as-is.
- Register a small service worker from a client component after mount.
- iOS Safari never fires beforeinstallprompt — render the share-sheet guide manually.
- Drop a hosted install popup in <head> so you never have to write UI code.
Before you start
- Next.js 13.4+
- HTTPS origin (Vercel, Netlify, self-hosted with TLS)
- Two square icons (192px + 512px)
manifest.webmanifest
{
"name": "Your product",
"short_name": "Product",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#111827",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
]
}Step-by-step for Next.js
- Step 1Add the manifest to /public
Place manifest.webmanifest in /public so it is served at the origin root. Next.js does not process it.
- Step 2Link the manifest from the root layout
In the App Router, add the link tag inside app/layout.tsx metadata; in the Pages Router, use next/head inside _document.tsx.
// app/layout.tsx export const metadata = { manifest: '/manifest.webmanifest', themeColor: '#111827', }; - Step 3Register the service worker on the client
Create a small client component that registers /sw.js after mount, then render it once at the root.
'use client'; import { useEffect } from 'react'; export function RegisterSW() { useEffect(() => { if ('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js'); }, []); return null; } - Step 4Add the install prompt
Either roll your own beforeinstallprompt UI (Chromium only) or drop InstantPWA in <head> to get cross-platform prompts including iOS instructions.
// app/layout.tsx – hosted install popup, one line <script src="https://instantpwa.com/api/public/embed.js?id=YOUR_ID" async />
Minimal sw.js
// public/sw.js
self.addEventListener('install', (e) => self.skipWaiting());
self.addEventListener('activate', (e) => self.clients.claim());
self.addEventListener('fetch', () => {});Common Next.js gotchas
- Next.js sometimes caches manifests at the CDN edge — bump the URL with a query string when you change icons.
- The service worker scope is the folder the file is served from. Keep sw.js at the origin root, never under /app or /_next.
- In dev, Chrome disables install prompts on http://localhost after several dismissals — test on a real HTTPS preview.
FAQ
Do I need a native app if my Next.js site is a PWA?
No. Once your Next.js site meets the three PWA criteria (HTTPS, a manifest, a service worker), Chromium browsers offer to install it and iOS Safari 16.4+ supports the full standalone experience — including web push.
Will a service worker slow my Next.js site down?
Not measurably on first load. The worker installs asynchronously after the page becomes interactive. On repeat visits it typically improves LCP and TTFB because assets are served from cache.
Can I still deploy my Next.js site to Vercel, Netlify or Cloudflare?
Yes. Any static or edge host works — the manifest and service worker are plain files served from the origin. No build-time integration is required.
Skip the setup — drop in one line
InstantPWA handles the install prompt across iOS, Android and desktop. Free forever for one popup.
Get started free