Skip to main content

Best PWA Boilerplates and Starter Kits 2026

·StarterPick Team
pwaboilerplatenextjsviteservice-worker

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

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

Comments