Skip to main content

Best PWA Boilerplates and Starter Kits 2026

·StarterPick Team
Share:

Best PWA Boilerplates and Starter Kits in 2026

TL;DR

Progressive Web Apps have had a remarkable comeback in 2026. Apple's WebKit team finally implemented full Push Notifications on iOS 16.4+, the Web Share Target API covers the most critical "native-like" gaps, and Lighthouse PWA scores now factor into Core Web Vitals reports. The best PWA starters today are not separate "PWA frameworks" — they're Next.js, Vite, or SvelteKit apps with next-pwa or vite-plugin-pwa bolted on correctly. The biggest trap is adding a service worker after the fact and breaking your caching strategy. Start with a PWA-ready template from day one if you need offline support, installability, or push notifications.

Key Takeaways

  • vite-plugin-pwa is the gold standard for Vite, React, Vue, and SvelteKit apps — auto-generates service workers with Workbox, handles precaching, and provides excellent TypeScript support
  • next-pwa (by DucanArte) is the maintained Next.js PWA plugin after the original next-pwa package was abandoned — works with App Router in Next.js 15
  • PWA is not a replacement for native when you need deep hardware access (Bluetooth, NFC, ARKit) — but it covers 85% of "app-like" experiences
  • iOS 16.4+ finally supports Web Push — the last major gap that made iOS PWAs second-class is now closed
  • App Shell + Offline Page is the minimum viable PWA — cache the shell, show a custom offline page, add install prompt
  • Manifest + HTTPS + Service Worker are the three requirements for installability — all PWA starters handle these automatically

What Makes a Modern PWA in 2026

A PWA is just a web app that meets three criteria for browser installability:

  1. HTTPS — required for service workers
  2. Web App Manifestmanifest.json with name, icons, theme color, display mode
  3. Service Worker — handles offline caching, push notifications, background sync

Modern PWAs go further:

  • Offline support — app shell caches all UI assets; API calls fall back to cached data
  • Push notifications — via Web Push API (now works on iOS 16.4+)
  • Install promptbeforeinstallprompt event for a custom "Add to Home Screen" button
  • Background sync — queue form submissions when offline, sync when reconnected
  • Share Target — app appears in the OS share sheet

The Top PWA Starters for 2026

1. Vite PWA Template (with vite-plugin-pwa)

The most production-ready starting point for React or Vue PWAs is the official vite-plugin-pwa with its starter templates.

Setup:

# Create a Vite React app
npm create vite@latest my-pwa -- --template react-ts
cd my-pwa

# Install vite-plugin-pwa
npm install -D vite-plugin-pwa

vite.config.ts:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({
  plugins: [
    react(),
    VitePWA({
      registerType: 'autoUpdate',
      workbox: {
        globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
        runtimeCaching: [
          {
            urlPattern: /^https:\/\/api\.yourdomain\.com\/.*/i,
            handler: 'NetworkFirst',
            options: {
              cacheName: 'api-cache',
              expiration: { maxEntries: 100, maxAgeSeconds: 60 * 60 * 24 },
              cacheableResponse: { statuses: [0, 200] },
            },
          },
        ],
      },
      manifest: {
        name: 'My PWA App',
        short_name: 'MyApp',
        description: 'A Progressive Web App',
        theme_color: '#1a1a2e',
        background_color: '#ffffff',
        display: 'standalone',
        icons: [
          { src: 'pwa-192x192.png', sizes: '192x192', type: 'image/png' },
          { src: 'pwa-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'any maskable' },
        ],
      },
    }),
  ],
})

Why it wins: Workbox handles all the caching complexity. NetworkFirst for API calls means users always get fresh data when online but stale cache when offline. The autoUpdate register type silently updates the service worker without disrupting the user.

The official starter repos:

2. SvelteKit PWA Starter

SvelteKit has the cleanest PWA story of any meta-framework in 2026. The @vite-pwa/sveltekit plugin integrates directly with SvelteKit's build and routing.

npm create svelte@latest my-pwa
cd my-pwa
npm install -D @vite-pwa/sveltekit

svelte.config.js:

import { SvelteKitPWA } from '@vite-pwa/sveltekit'

const config = {
  kit: {
    adapter: adapter(),
  },
  vite: {
    plugins: [
      SvelteKitPWA({
        registerType: 'autoUpdate',
        manifest: { name: 'SvelteKit PWA', short_name: 'SKPWA', display: 'standalone' },
        workbox: { globPatterns: ['**/*.{js,css,html,svg,png,ico,txt}'] },
      }),
    ],
  },
}

SvelteKit's file-based routing and SSR combine particularly well with the App Shell caching model — the shell is served from cache, dynamic content loads from the network.

3. Next.js PWA with @ducanh2912/next-pwa

The original next-pwa by Shadowwalker was abandoned after Next.js 13. The maintained fork @ducanh2912/next-pwa works with Next.js 14/15 App Router:

npx create-next-app@latest my-next-pwa
npm install @ducanh2912/next-pwa

next.config.js:

const withPWA = require('@ducanh2912/next-pwa').default({
  dest: 'public',
  cacheOnFrontEndNav: true,
  aggressiveFrontEndNavCaching: true,
  reloadOnOnline: true,
  disable: process.env.NODE_ENV === 'development',
  workboxOptions: {
    disableDevLogs: true,
  },
})

module.exports = withPWA({
  // your Next.js config
})

Limitation with App Router: Next.js App Router uses streaming SSR and React Server Components that don't cache well in Workbox's current implementation. The App Shell pattern works best with Page Router. For App Router, use the PWA primarily for manifest + install prompt + push notifications rather than aggressive offline caching.

4. Create PWA App (Standalone)

For apps that don't need a meta-framework — pure client-side PWAs like offline tools, games, or apps that authenticate entirely client-side — create-pwa from Microsoft's PWABuilder team is excellent:

npx @pwabuilder/pwa-starter create my-app

The PWABuilder starter uses Lit + Vite and generates:

  • Service worker with Workbox
  • Web App Manifest
  • iOS/Android icon set
  • Windows taskbar integration metadata
  • Shortcuts for the install menu

PWABuilder also has a web tool at pwabuilder.com that takes any URL and generates the manifest, icons, and deployment packages for the Microsoft Store, Google Play (via TWA), and Apple App Store (via PWA-to-native wrappers).


Adding Push Notifications to Any PWA

iOS 16.4+ finally supports Web Push — which means you can now send push notifications to users on both Android and iOS with a single web push implementation:

// client — request notification permission and subscribe
async function subscribeToPush() {
  const registration = await navigator.serviceWorker.ready

  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY),
  })

  // Send subscription to your server
  await fetch('/api/push/subscribe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(subscription),
  })
}

// service-worker.js — handle incoming push events
self.addEventListener('push', (event) => {
  const data = event.data?.json()
  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/pwa-192x192.png',
      badge: '/badge-72x72.png',
      data: { url: data.url },
    })
  )
})

self.addEventListener('notificationclick', (event) => {
  event.notification.close()
  event.waitUntil(clients.openWindow(event.notification.data.url))
})

Server (web-push library):

import webPush from 'web-push'

webPush.setVapidDetails(
  'mailto:your@email.com',
  process.env.VAPID_PUBLIC_KEY,
  process.env.VAPID_PRIVATE_KEY
)

// Send a notification to a saved subscription
await webPush.sendNotification(subscription, JSON.stringify({
  title: 'New message',
  body: 'Alice sent you a message',
  url: '/messages/123',
}))

The App Shell Architecture

The App Shell is the minimal HTML, CSS, and JavaScript required to render the UI structure. It's cached on first load and served from cache on every subsequent visit — making the app feel instantaneous.

User visits app for the first time:
1. Browser fetches app shell (HTML + CSS + JS bundle) from network
2. Service worker installs and caches the shell
3. Content loads from network

User visits app subsequently:
1. Service worker intercepts request
2. Returns app shell from cache IMMEDIATELY (< 1ms)
3. Content loads from network (or cache if offline)

Implementation with Workbox:

// In vite.config.ts (vite-plugin-pwa)
VitePWA({
  workbox: {
    // App Shell: precache all static assets on install
    globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],

    // Runtime caching strategies
    runtimeCaching: [
      // API calls: try network first, fall back to cache
      {
        urlPattern: /\/api\/.*/,
        handler: 'NetworkFirst',
        options: { cacheName: 'api-cache', networkTimeoutSeconds: 3 },
      },
      // Static assets: serve from cache, refresh in background
      {
        urlPattern: /\.(png|jpg|webp|svg|gif)$/,
        handler: 'CacheFirst',
        options: {
          cacheName: 'image-cache',
          expiration: { maxEntries: 200, maxAgeSeconds: 30 * 24 * 60 * 60 },
        },
      },
    ],
  },
})

Key caching strategies:

StrategyUse ForTrade-off
CacheFirstStatic assets (images, fonts)Always fast, may serve stale
NetworkFirstAPI dataFresh when online, slow if offline
StaleWhileRevalidateNon-critical contentInstant response + background refresh
NetworkOnlyAuth, payments, mutationsNever cached — fails offline

Custom Install Prompt: The Right UX

The browser's default install banner appears once and is easily dismissed. Build a custom install prompt that appears at the right moment:

// hooks/usePwaInstall.ts
import { useEffect, useState } from 'react'

interface BeforeInstallPromptEvent extends Event {
  prompt(): Promise<void>
  userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>
}

export function usePwaInstall() {
  const [installPrompt, setInstallPrompt] = useState<BeforeInstallPromptEvent | null>(null)
  const [isInstalled, setIsInstalled] = useState(false)

  useEffect(() => {
    // Capture the browser's install event
    const handleBeforeInstall = (e: Event) => {
      e.preventDefault()
      setInstallPrompt(e as BeforeInstallPromptEvent)
    }

    // Detect if already installed (display=standalone means launched from home screen)
    if (window.matchMedia('(display-mode: standalone)').matches) {
      setIsInstalled(true)
    }

    window.addEventListener('beforeinstallprompt', handleBeforeInstall)
    window.addEventListener('appinstalled', () => setIsInstalled(true))

    return () => window.removeEventListener('beforeinstallprompt', handleBeforeInstall)
  }, [])

  const install = async () => {
    if (!installPrompt) return
    await installPrompt.prompt()
    const { outcome } = await installPrompt.userChoice
    if (outcome === 'accepted') setIsInstalled(true)
    setInstallPrompt(null)
  }

  return { canInstall: !!installPrompt && !isInstalled, isInstalled, install }
}

// Usage — show the prompt after the user has completed a meaningful action
function AfterSignupModal() {
  const { canInstall, install } = usePwaInstall()

  if (!canInstall) return null

  return (
    <div className="install-prompt">
      <p>Add this app to your home screen for the best experience</p>
      <button onClick={install}>Install App</button>
    </div>
  )
}

Best practices for install prompt timing:

  • Show after the user completes signup or logs in (they've committed to the product)
  • Show after 3+ visits (proven engagement)
  • Never show on first page load — it's too early
  • Always provide a "Not now" option and suppress for 30+ days

PWA Checklist for Production

Before shipping your PWA:

RequirementCheckNotes
HTTPSRequired; localhost passes for development
Web App ManifestAll required fields, correct display mode
Icons: 192x192 + 512x512Plus maskable variant for Android adaptive icons
Service Worker registeredWorkbox handles all caching logic
Offline fallback pageCustom 404/offline page when network unavailable
Lighthouse PWA score ≥ 90Run npx lighthouse to verify
iOS meta tagsapple-mobile-web-app-capable, status bar style
Theme colorMatches brand, used in browser chrome
Push notificationsOptionaliOS 16.4+ required for iOS

PWA vs Native App: When Each Wins

FactorPWANative (iOS/Android)
Development costOne codebaseTwo codebases (or React Native)
App Store distributionNo store requiredDiscovery via App Store
Push notifications✅ iOS 16.4+✅ Full support
Offline support✅ via Service Worker✅ Full support
Bluetooth/NFC access❌ No✅ Full support
Camera/ARLimited✅ Full ARKit/ARCore
PerformanceGood (60fps for most use cases)Excellent for complex animations
Install frictionLow (browser banner)Higher (App Store)

PWA wins for: SaaS dashboards, content apps, tools, form-based workflows, anything where the logic is the product (not the graphics).

Native wins for: Games, AR/VR, apps needing Bluetooth or NFC, apps where 60fps complex animations matter.


Methodology

  • Sources: vite-plugin-pwa documentation, web.dev PWA guides, Chrome developer blog, Apple WebKit release notes (iOS 16.4 Web Push), MDN Web Docs
  • Framework versions: Vite 6.x, Next.js 15.x, SvelteKit 2.x
  • Data: npm download trends from npmjs.com, March 2026

PWA in 2026: Browser Support and Platform Reality

The browser support story for PWAs improved significantly with iOS 16.4, which added Web Push Notifications support in Safari. Prior to this, push notifications on iOS required a native app, which was the hardest feature gap between PWA and native for many SaaS products. The remaining gaps — Bluetooth, NFC, background sync with complex conditions — are relevant for a minority of SaaS use cases.

Android's PWA support is comprehensive and has been for years. Chrome on Android handles install prompts, push notifications, offline caching, and file system access. The install flow on Android is less friction than the App Store process: users tap the browser banner and confirm. No app review process, no 30% platform cut on digital product sales.

For SaaS products targeting productivity tools, field service workers, or any workflow where offline capability is a feature rather than a nice-to-have, PWA is now a production-ready choice for iOS and Android without the native app maintenance overhead. The primary remaining case for React Native or native apps is when hardware access (camera, AR, Bluetooth, NFC), complex animations, or App Store distribution and discoverability are product requirements.

PWA boilerplates sit at the intersection of the SaaS boilerplate market and the mobile-first development ecosystem. For the broader boilerplate context, best Next.js boilerplates 2026 covers which Next.js starters include PWA configuration out of the box, and best SaaS boilerplates 2026 provides the full market view including frameworks beyond Next.js.


Service Worker Update Strategies

The most user-visible PWA operational issue after launch is service worker updates. When you deploy a new version of your app, existing users have a cached service worker running the old version. How you handle the transition determines whether users get stuck on a stale version or experience jarring mid-session refreshes.

There are three standard strategies, each with different tradeoffs:

autoUpdate (used in most starter templates): the new service worker activates immediately when detected. Users on the page during a deploy get the new version on next navigation. The risk is that users in the middle of a multi-step flow (checkout, form submission) may see unexpected behavior if the new version changed the relevant routes. For simple content apps, autoUpdate is the right default.

prompt: the new service worker waits and your UI shows a "New version available — refresh" banner. The user controls when the update happens. This is the right choice for apps with complex client-side state where a mid-session update would lose work. The implementation requires a useRegisterSW hook from vite-plugin-pwa and a custom update notification component.

skipWaiting + custom event: the most flexible approach. The service worker broadcasts a SKIP_WAITING message, and your app listens for it to trigger a controlled page reload at a safe moment. This pattern is appropriate for apps with real-time data (WebSocket connections, collaborative editing) where you need to coordinate the update with the server.

The vite-plugin-pwa handles all three via its registerType configuration. The starter templates default to autoUpdate — change this to prompt for any app where "update happened unexpectedly while I was filling out a form" is a realistic user complaint.

Testing PWA Behavior During Development

PWA features (service workers, install prompts, push notifications) are notoriously hard to test in development because service workers require HTTPS (with localhost as the only HTTP exception) and caching behavior in development gets in the way of iteration. Most PWA starters disable service workers in development (disable: process.env.NODE_ENV === 'development') for this reason.

For testing PWA features, use Chrome DevTools's Application panel. The Service Workers section shows the current registration state, lets you trigger updates, and provides a bypass mode for testing without the cache interfering. The Manifest section validates your manifest.json — Chrome flags missing required fields and icon size issues directly there.

For push notification testing, use Chrome's DevTools Application → Push Messaging panel, which lets you trigger a push event to your registered service worker without a real push server. This is the fastest way to verify that your push event handler and showNotification call are working correctly before wiring up a real push backend.

The Lighthouse PWA audit (DevTools → Lighthouse → Progressive Web App) is the definitive checklist before shipping. Run it against your production build on a preview URL, not against localhost — some PWA criteria (HTTPS, manifest start URL) behave differently in production.

For the broader boilerplate context that includes PWA options, best SaaS boilerplates 2026 indicates which commercial starters include PWA configuration. The NextJS SaaS tech stack 2026 guide covers how PWA fits into the Next.js app decision. And best free open-source SaaS boilerplates 2026 includes several open-source starters with Vite PWA configurations that are worth studying as real implementations before you build your own.

Find PWA-ready boilerplates and starter kits on StarterPick — filter by offline support and push notifications.

Related: Best Next.js Boilerplates 2026 · Best SvelteKit Boilerplates 2026 · Edge-First SaaS Boilerplates: Cloudflare Workers 2026

The SaaS Boilerplate Matrix (Free PDF)

20+ SaaS starters compared: pricing, tech stack, auth, payments, and what you actually ship with. Updated monthly. Used by 150+ founders.

Join 150+ SaaS founders. Unsubscribe in one click.