Skip to content

Add PWA support to web frameworks

Published

This guide adds a web app manifest and a service worker to projects built with Next.js, Nuxt, SvelteKit, or Angular. Each framework keeps its own build integration, while the browser-facing result uses the same manifest and Service Worker APIs.

MDN marks the Service Worker API as widely available. Registration requires a secure context, and browsers treat localhost as secure for local development. The examples therefore keep the application usable over the network when registration is absent or fails.

Choose the subsection for the framework that owns the application’s build, then use the final subsection to preserve an online path when browser registration is unavailable.

Add a manifest route in Next.js App Router

Section titled “Add a manifest route in Next.js App Router”

Next.js provides a typed manifest route through app/manifest.ts. Its PWA guide creates a custom service worker at lib/service-worker.js and registers it from a client component with new URL("../lib/service-worker.js", import.meta.url).

Create the manifest with the fields used by the application. Put the referenced icon files in public/.

app/manifest.ts
import type { MetadataRoute } from "next";
export default function manifest(): MetadataRoute.Manifest {
return {
name: "Field Notes",
short_name: "Notes",
start_url: "/",
display: "standalone",
background_color: "#ffffff",
theme_color: "#12372a",
icons: [
{ src: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ src: "/icon-512.png", sizes: "512x512", type: "image/png" },
],
};
}

Add lib/service-worker.js, then use the Next.js registration example below for capability detection and failure handling.

The Nuxt modules directory documents @vite-pwa/nuxt for Nuxt 3. Add the module, then put manifest data under pwa.

Install it with npx nuxi@latest module add @vite-pwa/nuxt, then keep the configuration in nuxt.config.ts.

nuxt.config.ts
export default defineNuxtConfig({
modules: ["@vite-pwa/nuxt"],
pwa: {
registerType: "autoUpdate",
manifest: {
name: "Field Notes",
short_name: "Notes",
start_url: "/",
display: "standalone",
theme_color: "#12372a",
icons: [
{ src: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ src: "/icon-512.png", sizes: "512x512", type: "image/png" },
],
},
},
});

The module generates the service worker and injects the web app manifest from this configuration.

The Vite PWA integration for SvelteKit supplies SvelteKitPWA. Its documentation configures the plugin in vite.config.ts and exposes manifest markup through virtual:pwa-info.

Install @vite-pwa/sveltekit as a development dependency, then add the plugin after sveltekit().

vite.config.ts
import { sveltekit } from "@sveltejs/kit/vite";
import { SvelteKitPWA } from "@vite-pwa/sveltekit";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
sveltekit(),
SvelteKitPWA({
manifest: {
name: "Field Notes",
short_name: "Notes",
start_url: "/",
display: "standalone",
theme_color: "#12372a",
icons: [
{ src: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ src: "/icon-512.png", sizes: "512x512", type: "image/png" },
],
},
}),
],
});

Add the generated manifest link to the root layout as documented by the plugin.

src/routes/+layout.svelte
<script>
import { pwaInfo } from "virtual:pwa-info";
$: webManifestLink = pwaInfo ? pwaInfo.webManifest.linkTag : "";
</script>
<svelte:head>
{@html webManifestLink}
</svelte:head>

The empty-string fallback leaves the layout valid when the virtual module has no manifest information.

Angular’s @angular/pwa schematic adds the service-worker package, build support, registration, manifest link, icons, and ngsw-config.json.

Apply the schematic from the project directory and create a production build.

Terminal window
ng add @angular/pwa
ng build

Review the generated manifest.webmanifest, icons, and ngsw-config.json rather than treating their starter values as application metadata. Angular’s guide uses a production configuration when demonstrating the service worker locally.

Register the Next.js worker and preserve the network path

Section titled “Register the Next.js worker and preserve the network path”

Framework plugins automate build output, but the browser still exposes service-worker capability through navigator.serviceWorker. This Next.js Client Component follows the framework guide’s worker URL and leaves normal network navigation in place when registration is unavailable or fails.

Save the component as app/service-worker-registration.tsx, alongside the lib/service-worker.js file named above, and render <ServiceWorkerRegistration /> from a layout or page.

app/service-worker-registration.tsx
"use client";
import { useEffect, useState } from "react";
export default function ServiceWorkerRegistration() {
const [status, setStatus] = useState("Checking service worker support…");
useEffect(() => {
if (!("serviceWorker" in navigator)) {
setStatus("Service worker registration is unavailable; the app remains online.");
return;
}
let cancelled = false;
navigator.serviceWorker
.register(
new URL("../lib/service-worker.js", import.meta.url),
{ scope: "/", updateViaCache: "none" },
)
.then(() => {
if (!cancelled) setStatus("Service worker registration is ready.");
})
.catch((error) => {
if (!cancelled) {
setStatus("Service worker registration failed; the app remains online.");
}
console.error(error);
});
return () => {
cancelled = true;
};
}, []);
return <p role="status">{status}</p>;
}

Use this direct registration for the Next.js custom worker. Nuxt and SvelteKit plugins, and Angular’s schematic, provide their own registration path; keep the same support and error states in the interface they initialize.