Web app manifest file location and format
Create a web app manifest file at app/manifest.ts or app/manifest.json. The file should export a default function that returns a MetadataRoute.Manifest object containing name, short_name, description, start_url, display mode, background_color, theme_color, and icons array. This enables users to install the PWA on their home screen.
Web app manifest required properties
A web app manifest must include: name (full application name), short_name (short version), description (app purpose), start_url (initial page when launched), display (e.g., 'standalone'), background_color (hex color for splash screen), theme_color (hex color for browser chrome), and icons (array of objects with src, sizes, and type properties). Icons should be placed in the public/ folder.
Web Push Notifications browser support
Web Push Notifications are supported on iOS 16.4+ for home screen installed apps, Safari 16 for macOS 13 or later, Chromium-based browsers, and Firefox. PWAs with push notifications can re-engage users even when the app is not actively in use, making them viable alternatives to native apps without requiring offline support for installation prompts.
Push notification component implementation pattern
Create a push notification manager as a client component that: checks for serviceWorker and PushManager support, registers the service worker from lib/service-worker.js with scope '/' and updateViaCache 'none', subscribes to push using registration.pushManager.subscribe() with userVisibleOnly true and applicationServerKey from NEXT_PUBLIC_VAPID_PUBLIC_KEY, handles subscription state, and calls Server Actions for subscribe/unsubscribe/send operations.
URL base64 to Uint8Array conversion for VAPID keys
To convert a base64-encoded VAPID public key to Uint8Array for the PushManager API, use this utility: add padding with '='.repeat((4 - (base64String.length % 4)) % 4), replace '-' with '+' and '_' with '/', decode with window.atob(), then create a Uint8Array from the raw data by iterating through and calling charCodeAt() on each character.
Server Actions for Web Push subscriptions
Create Server Actions in app/actions.ts: subscribeUser(sub: PushSubscription) stores the subscription (typically in a database for production), unsubscribeUser() removes the subscription, and sendNotification(message: string) sends a push notification using the web-push library. Use webpush.setVapidDetails() with email, NEXT_PUBLIC_VAPID_PUBLIC_KEY, and VAPID_PRIVATE_KEY environment variables.
Generating VAPID keys for Web Push
Install web-push globally (pnpm add -g web-push, npm install -g web-push, yarn global add web-push, or bun add -g web-push), then run 'web-push generate-vapid-keys' to generate public and private key pairs. Add NEXT_PUBLIC_VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY to .env file.
Service worker push event handling
The service worker listens to 'push' events. When data arrives, parse it with event.data.json(), extract title and body, create options with body, icon, badge, vibrate pattern, and data properties, then call event.waitUntil(self.registration.showNotification(title, options)). The 'notificationclick' event handler closes the notification and opens a specified URL with clients.openWindow().
Service worker notification options
When showing notifications, options can include: body (notification text), icon (main icon URL), badge (badge icon URL), vibrate (vibration pattern array, e.g., [100, 50, 100]), and data (custom data object attached to notification with dateOfArrival, primaryKey, etc.).
iOS home screen installation instructions
Create an InstallPrompt component that detects iOS devices using /iPad|iPhone|iPod/.test(navigator.userAgent) and checks if already installed with window.matchMedia('(display-mode: standalone)').matches. For iOS, display instructions to tap the share button (⎋) then 'Add to Home Screen' (➕). Do not show the prompt if isStandalone is true.
Requirements for PWA home screen installation
To enable PWA installation to mobile home screen, the application must have: 1) a valid web app manifest file, and 2) be served over HTTPS. Modern browsers automatically show installation prompts when these criteria are met. The beforeinstallprompt event is available but not recommended as it lacks cross-browser and platform support (does not work on Safari iOS).
Testing PWA with local HTTPS and notifications
For local testing: run with 'next dev --experimental-https' to enable HTTPS, ensure the browser (Chrome, Safari, Firefox) has notifications enabled, accept notification permissions when prompted, verify notifications are not disabled globally in browser settings, and try a different browser if notifications still do not appear.
Global security headers for PWA
Configure in next.config.js with async headers() returning an array. For all routes (source: '/(.*)'): X-Content-Type-Options: 'nosniff' prevents MIME sniffing, X-Frame-Options: 'DENY' blocks clickjacking, Referrer-Policy: 'strict-origin-when-cross-origin' controls referrer info sharing.
Service worker security headers configuration
For the service worker at source '/sw.js', set: Content-Type: 'application/javascript; charset=utf-8' ensures correct interpretation, Cache-Control: 'no-cache, no-store, must-revalidate' prevents caching to ensure users always get the latest version, Content-Security-Policy: "default-src 'self'; script-src 'self'" restricts scripts to same origin only.
PWA offline support with useOffline hook
Next.js provides an experimental useOffline hook and experimental.useOffline config option for connectivity-aware UI and automatic retries of failed navigation and Server Action requests. For full service-worker-based offline caching, consider using Serwist library which provides Next.js integration examples for both Turbopack and webpack.
Static exports with PWA considerations
When using Next.js static exports (for applications not running a server), migrate from Server Actions to calling an external API instead, and move security headers configuration from next.config.js to your proxy configuration.