Skip to content

Setting Up @vite-pwa/astro

@vite-pwa/astro is an Astro integration built on top of Vite PWA. It wires a service worker and a web app manifest directly into Astro’s build pipeline, so you get full PWA support with almost no manual configuration. Because Astro is an SSG framework, the integration pre-caches your built output automatically — pages, scripts, stylesheets, and assets are all available offline right after the first visit.

Install the package with your preferred package manager:

Terminal window
npm install -D @vite-pwa/astro
# or
pnpm add -D @vite-pwa/astro

Open astro.config.mjs and add AstroPWA to the integrations array:

import { defineConfig } from 'astro/config'
import AstroPWA from '@vite-pwa/astro'
export default defineConfig({
integrations: [
AstroPWA({
registerType: 'autoUpdate',
manifest: {
name: 'My Astro PWA',
short_name: 'AstroPWA',
theme_color: '#ffffff',
icons: [
{ src: 'pwa-192x192.png', sizes: '192x192', type: 'image/png' },
{ src: 'pwa-512x512.png', sizes: '512x512', type: 'image/png' },
],
},
workbox: {
globPatterns: ['**/*.{css,js,html,svg,png,ico}'],
},
devOptions: {
enabled: true,
},
}),
],
})

registerType controls how the service worker handles updates when a new version is deployed.

  • 'autoUpdate' — The service worker installs and activates silently in the background. Users always get the latest version without seeing any prompt.
  • 'prompt' — The service worker waits for your code to confirm the update. Use virtual:pwa-register to present a “reload to update” button to the user.
src/pwa.ts
import { registerSW } from 'virtual:pwa-register'
registerSW({ immediate: true })

Import src/pwa.ts in your root layout to activate the registration logic on every page.

The manifest object is injected as a <link rel="manifest"> tag and served as manifest.webmanifest. Fill in at minimum name, short_name, theme_color, and icons. The integration validates the manifest at build time and will warn you about missing required fields.

By default, @vite-pwa/astro uses the workbox strategy, which generates a complete service worker for you. The workbox.globPatterns array tells Workbox which built files to add to the precache manifest — ['**/*.{css,js,html,svg,png,ico}'] covers all common static asset types.

If you need full control over your service worker logic — custom routing, background sync, push handling — switch to the injectManifest strategy:

AstroPWA({
strategies: 'injectManifest',
srcDir: 'src',
filename: 'sw.ts',
manifest: { /* ... */ },
})

With injectManifest, you write your own src/sw.ts. At build time, Vite PWA compiles it and injects the precache manifest list into it. You get both flexibility and automatic precaching.

By default, the service worker is disabled in the Vite dev server because hot-module replacement and a caching SW do not mix well. Set devOptions.enabled: true to register the SW during development so you can verify offline behaviour and manifest metadata without running a production build first.

AstroPWA({
// ...other options
devOptions: {
enabled: true,
},
})
OptionBenefitCost
Use the full @vite-pwa/astro packageSets up service worker + manifest in one step, plugs straight into Astro’s build pipelineTies you to the Vite PWA ecosystem, you have to keep the plugin updated yourself
Configure the service worker manually in AstroNot tied to a specific plugin, full controlYou must integrate with Astro’s build output entirely by hand, risking bugs from asset hashes falling out of sync
  • Forgetting to turn on devOptions.enabled: true, so the service worker cannot be tested on the dev server at all, leading to a false impression that the setup is broken.
  • Not realizing Astro defaults to SSG, so precaching only really happens at build time — testing in dev mode and being confused about why nothing is cached.
  • Setting registerType to the wrong value for the intended UX (e.g. autoUpdate when you actually wanted a user-facing prompt), so the update experience does not match what was designed.

@vite-pwa/astro is part of the same Vite PWA ecosystem that Elk, the open-source Mastodon client, uses on its own stack (Elk’s package.json lists vite-plugin-pwa), and many VitePress and Astro documentation sites use the same kind of setup to become installable in just a few lines of config.

Which command installs the @vite-pwa/astro package as a dev dependency using npm?
What does registerType: "autoUpdate" do?
What is the key difference between the workbox and injectManifest strategies?
What is the purpose of devOptions: { enabled: true }?