├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .npmrc ├── .vitepress ├── components.d.ts ├── config.ts ├── contributors.ts ├── integrations.ts ├── scripts │ ├── pwa.ts │ └── transformHead.ts └── theme │ ├── PwaLayout.vue │ ├── components │ ├── ChangeLog.md │ ├── CleanupOutdatedCaches.md │ ├── ExamplesBehaviors.md │ ├── ExamplesGenerateSW.md │ ├── ExamplesInjectManifest.md │ ├── GenerateSWCleanupOutdatedCaches.md │ ├── GenerateSWSourceMap.md │ ├── HeuristicWorkboxWindow.md │ ├── HomePage.vue │ ├── InjectManifestBuild.md │ ├── InjectManifestCleanupOutdatedCaches.md │ ├── InjectManifestSourceMap.md │ ├── PromptForUpdateImg.vue │ ├── ReactReactiveWarning.md │ ├── ReloadPrompt.vue │ ├── RunExamples.md │ ├── ScaffoldingPWAProject.md │ ├── ServiceWorkerClientErrors.md │ ├── SsrSsg.md │ ├── TypeScriptError2307.md │ └── vp │ │ ├── NavBarTitle.vue │ │ └── TeamMembersItem.vue │ ├── index.ts │ └── styles │ ├── main.css │ └── vars.css ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets-generator ├── api.md ├── cli.md ├── index.md ├── integrations.md └── migrations.md ├── assets ├── close-bundle-hook.svg ├── inject-manifest.svg ├── vite-build-cli.svg └── vite-config-file.svg ├── avatars ├── antfu.png ├── create-webp.mjs ├── hannoeru.png └── userquin.png ├── deployment ├── apache.md ├── aws.md ├── index.md ├── netlify.md ├── nginx.md └── vercel.md ├── examples ├── astro.md ├── iles.md ├── index.md ├── nuxt.md ├── preact.md ├── qwik.md ├── react.md ├── remix.md ├── solidjs.md ├── svelte.md ├── sveltekit.md ├── vitepress.md └── vue.md ├── frameworks ├── astro.md ├── iles.md ├── index.md ├── nuxt.md ├── preact.md ├── qwik.md ├── react.md ├── remix.md ├── solidjs.md ├── svelte.md ├── sveltekit.md ├── vitepress.md └── vue.md ├── guide ├── auto-update.md ├── change-log.md ├── cookbook.md ├── development.md ├── faq.md ├── index.md ├── inject-manifest.md ├── periodic-sw-updates.md ├── prompt-for-update.md ├── pwa-minimal-requirements.md ├── register-service-worker.md ├── scaffolding.md ├── service-worker-precache.md ├── service-worker-strategies-and-behaviors.md ├── service-worker-without-pwa-capabilities.md ├── static-assets.md ├── testing-service-worker.md └── unregister-service-worker.md ├── hero.png ├── index.md ├── netlify.toml ├── package.json ├── pnpm-lock.yaml ├── public ├── Cascadia.woff2 ├── Virgil.woff2 ├── _headers ├── apple-touch-icon.png ├── banner_dark.svg ├── banner_light.svg ├── favicon.ico ├── favicon.svg ├── icon_dark.svg ├── icon_gray.svg ├── icon_light.svg ├── integration-logos │ ├── astro.svg │ ├── iles.svg │ ├── nuxt.svg │ ├── remix.svg │ ├── sveltekit.svg │ └── vitepress.svg ├── maskable-icon.png ├── netlify.svg ├── og-image.png ├── prompt-update.png ├── pwa-192x192.png ├── pwa-512x512.png ├── pwa-64x64.png ├── robots.txt ├── safari-pinned-tab.svg ├── shortcuts │ ├── assets.png │ ├── assets.svg │ ├── deploy.png │ ├── deploy.svg │ ├── frameworks.png │ ├── frameworks.svg │ ├── guide.png │ ├── guide.svg │ ├── workbox.png │ └── workbox.svg ├── team-avatars │ ├── antfu.webp │ ├── hannoeru.webp │ └── userquin.webp └── vite-plugin-pwa.excalidraw ├── pwa-assets.config.ts ├── tsconfig.json ├── vite.config.ts └── workbox ├── generate-sw.md ├── index.md └── inject-manifest.md /.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | dev-dist/ 4 | node_modules/ 5 | *.d.ts 6 | !.vitepress 7 | .vitepress/dist/* 8 | .vitepress/cache/deps/*.* 9 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@antfu"], 3 | "overrides": [ 4 | { 5 | "files": [ 6 | "**/*.md/*.*" 7 | ], 8 | "rules": { 9 | "@stylistic/js/no-trailing-spaces": "off", 10 | "@typescript-eslint/no-this-alias": "off", 11 | "@typescript-eslint/no-unused-vars": "off", 12 | "@typescript-eslint/semi": "off", 13 | "@typescript-eslint/quotes": "off", 14 | "n/handle-callback-err": "off", 15 | "no-restricted-globals": "off", 16 | "no-restricted-syntax": "off", 17 | "no-labels": "off" 18 | } 19 | }, 20 | { 21 | "files": [ 22 | "**/*.ts" 23 | ], 24 | "rules": { 25 | "@typescript-eslint/no-unused-vars": "off" 26 | } 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | # intellij stuff 5 | .idea/ 6 | .vitepress/cache/ 7 | .vitepress/dist/ 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | auto-install-peers=true 4 | shell-emulator=true 5 | -------------------------------------------------------------------------------- /.vitepress/components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // @ts-nocheck 3 | // Generated by unplugin-vue-components 4 | // Read more: https://github.com/vuejs/core/pull/3399 5 | export {} 6 | 7 | /* prettier-ignore */ 8 | declare module 'vue' { 9 | export interface GlobalComponents { 10 | ChangeLog: typeof import('./theme/components/ChangeLog.md')['default'] 11 | CleanupOutdatedCaches: typeof import('./theme/components/CleanupOutdatedCaches.md')['default'] 12 | ExamplesBehaviors: typeof import('./theme/components/ExamplesBehaviors.md')['default'] 13 | ExamplesGenerateSW: typeof import('./theme/components/ExamplesGenerateSW.md')['default'] 14 | ExamplesInjectManifest: typeof import('./theme/components/ExamplesInjectManifest.md')['default'] 15 | GenerateSWCleanupOutdatedCaches: typeof import('./theme/components/GenerateSWCleanupOutdatedCaches.md')['default'] 16 | GenerateSWSourceMap: typeof import('./theme/components/GenerateSWSourceMap.md')['default'] 17 | HeuristicWorkboxWindow: typeof import('./theme/components/HeuristicWorkboxWindow.md')['default'] 18 | HomePage: typeof import('./theme/components/HomePage.vue')['default'] 19 | InjectManifestBuild: typeof import('./theme/components/InjectManifestBuild.md')['default'] 20 | InjectManifestCleanupOutdatedCaches: typeof import('./theme/components/InjectManifestCleanupOutdatedCaches.md')['default'] 21 | InjectManifestSourceMap: typeof import('./theme/components/InjectManifestSourceMap.md')['default'] 22 | NavBarTitle: typeof import('./theme/components/vp/NavBarTitle.vue')['default'] 23 | PromptForUpdateImg: typeof import('./theme/components/PromptForUpdateImg.vue')['default'] 24 | ReactReactiveWarning: typeof import('./theme/components/ReactReactiveWarning.md')['default'] 25 | ReloadPrompt: typeof import('./theme/components/ReloadPrompt.vue')['default'] 26 | RunExamples: typeof import('./theme/components/RunExamples.md')['default'] 27 | ScaffoldingPWAProject: typeof import('./theme/components/ScaffoldingPWAProject.md')['default'] 28 | ServiceWorkerClientErrors: typeof import('./theme/components/ServiceWorkerClientErrors.md')['default'] 29 | SsrSsg: typeof import('./theme/components/SsrSsg.md')['default'] 30 | TeamMembersItem: typeof import('./theme/components/vp/TeamMembersItem.vue')['default'] 31 | TypeScriptError2307: typeof import('./theme/components/TypeScriptError2307.md')['default'] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.vitepress/contributors.ts: -------------------------------------------------------------------------------- 1 | import type { DefaultTheme } from 'vitepress' 2 | 3 | export interface CoreTeam extends Partial { 4 | avatar: string 5 | name: string 6 | // required to download avatars from GitHub 7 | github: string 8 | bluesky?: string 9 | webtools?: string 10 | discord?: string 11 | youtube?: string 12 | sponsor?: string 13 | title?: string 14 | org?: string 15 | desc?: string 16 | } 17 | 18 | function createLinks(tm: CoreTeam): CoreTeam { 19 | tm.links = [{ icon: 'github', link: `https://github.com/${tm.github}` }] 20 | if (tm.bluesky) 21 | tm.links.push({ icon: 'bluesky', link: `https://bsky.app/profile/${tm.bluesky}` }) 22 | 23 | if (tm.webtools) 24 | tm.links.push({ icon: 'mastodon', link: `https://elk.zone/m.webtoo.ls/@${tm.webtools}` }) 25 | 26 | if (tm.discord) 27 | tm.links.push({ icon: 'discord', link: tm.discord }) 28 | 29 | if (tm.youtube) 30 | tm.links.push({ icon: 'youtube', link: `https://www.youtube.com/@${tm.youtube}` }) 31 | 32 | ;(tm as any).teamMember = true 33 | 34 | return tm 35 | } 36 | 37 | const plainTeamMembers = [ 38 | { 39 | avatar: '/team-avatars/antfu.webp', 40 | name: 'Anthony Fu', 41 | github: 'antfu', 42 | bluesky: 'antfu.me', 43 | webtools: 'antfu', 44 | youtube: 'antfu', 45 | discord: 'https://chat.antfu.me', 46 | sponsor: 'https://github.com/sponsors/antfu', 47 | title: 'A fanatical open sourceror, working', 48 | org: 'NuxtLabs', 49 | orgLink: 'https://nuxtlabs.com/', 50 | desc: 'Core team member of Vite & Vue', 51 | }, 52 | { 53 | avatar: '/team-avatars/userquin.webp', 54 | name: 'Joaquín Sánchez', 55 | github: 'userquin', 56 | webtools: 'userquin', 57 | bluesky: 'userquin.bsky.social', 58 | title: 'A fullstack and android developer', 59 | desc: 'Vite\'s fanatical follower', 60 | }, 61 | { 62 | avatar: '/team-avatars/hannoeru.webp', 63 | name: 'ハン / Han', 64 | github: 'hannoeru', 65 | bluesky: 'hannoeru.me', 66 | title: 'Student / Front-End Engineer', 67 | desc: '@windi_css member', 68 | }, 69 | ] 70 | 71 | const teamMembers = plainTeamMembers.map(tm => createLinks(tm)) 72 | 73 | export { teamMembers } 74 | -------------------------------------------------------------------------------- /.vitepress/integrations.ts: -------------------------------------------------------------------------------- 1 | import type { DefaultTheme } from 'vitepress' 2 | 3 | export interface Integration extends Partial { 4 | avatar: string 5 | name: string 6 | // required to download avatars from GitHub 7 | github: string 8 | mastodon?: string 9 | x?: string 10 | bluesky?: string 11 | title?: string 12 | org?: string 13 | desc?: string 14 | } 15 | 16 | function createLinks(i: Integration): Integration { 17 | i.links = [{ icon: 'github', link: `https://github.com/${i.github}` }] 18 | if (i.mastodon) 19 | i.links.push({ icon: 'mastodon', link: `https://elk.zone/m.webtoo.ls/@${i.mastodon}` }) 20 | 21 | if (i.bluesky) 22 | i.links.push({ icon: 'bluesky', link: `https://bsky.app/profile/${i.bluesky}` }) 23 | 24 | if (i.x) 25 | i.links.push({ icon: 'x', link: `https://x.com/${i.x}` }) 26 | 27 | return i 28 | } 29 | 30 | const plainIntegrations = [ 31 | { 32 | avatar: '/integration-logos/iles.svg', 33 | name: 'îles', 34 | github: 'ElMassimo/iles', 35 | x: 'ilesjs', 36 | title: 'The joyful site generator', 37 | }, 38 | { 39 | avatar: '/integration-logos/sveltekit.svg', 40 | name: 'SvelteKit', 41 | github: 'sveltejs/kit', 42 | bluesky: 'svelte.dev', 43 | title: 'The fastest way to build svelte apps', 44 | }, 45 | { 46 | avatar: '/integration-logos/vitepress.svg', 47 | name: 'VitePress', 48 | github: 'vuejs/vitepress', 49 | title: 'Vite & Vue Powered Static Site Generator', 50 | }, 51 | { 52 | avatar: '/integration-logos/astro.svg', 53 | name: 'Astro', 54 | github: 'withastro/astro', 55 | mastodon: 'astro', 56 | x: 'astrodotbuild', 57 | bluesky: 'astro.build', 58 | title: 'Build fast websites, faster', 59 | }, 60 | { 61 | avatar: '/integration-logos/nuxt.svg', 62 | name: 'Nuxt 3', 63 | github: 'nuxt/nuxt', 64 | mastodon: 'nuxt', 65 | x: '@nuxt_js', 66 | bluesky: 'nuxt.com', 67 | title: 'The Intuitive Web Framework', 68 | }, 69 | { 70 | avatar: '/integration-logos/remix.svg', 71 | name: 'Remix', 72 | github: 'remix-run/remix', 73 | x: 'remix_run', 74 | title: 'Build better websites with Remix and React Router', 75 | }, 76 | ] 77 | 78 | const integrations = plainIntegrations.map(i => createLinks(i)) 79 | 80 | export { integrations } 81 | -------------------------------------------------------------------------------- /.vitepress/scripts/pwa.ts: -------------------------------------------------------------------------------- 1 | import type { PwaOptions } from '@vite-pwa/vitepress' 2 | 3 | export const pwa: Partial = { 4 | outDir: '.vitepress/dist', 5 | registerType: 'prompt', 6 | includeManifestIcons: false, 7 | manifest: { 8 | id: '/', 9 | name: 'Vite PWA', 10 | short_name: 'Vite PWA', 11 | description: 'Zero-config PWA for Vite and the ecosystem', 12 | theme_color: '#ffffff', 13 | start_url: '/', 14 | lang: 'en-US', 15 | dir: 'ltr', 16 | orientation: 'natural', 17 | display: 'standalone', 18 | display_override: ['window-controls-overlay'], 19 | categories: ['development', 'developer tools'], 20 | icons: [ 21 | { 22 | src: 'pwa-64x64.png', 23 | sizes: '64x64', 24 | type: 'image/png', 25 | }, 26 | { 27 | src: 'pwa-192x192.png', 28 | sizes: '192x192', 29 | type: 'image/png', 30 | }, 31 | { 32 | src: 'pwa-512x512.png', 33 | sizes: '512x512', 34 | type: 'image/png', 35 | purpose: 'any', 36 | }, 37 | { 38 | src: 'maskable-icon.png', 39 | sizes: '512x512', 40 | type: 'image/png', 41 | purpose: 'maskable', 42 | }, 43 | ], 44 | screenshots: [{ 45 | src: 'og-image.png', 46 | sizes: '1200x630', 47 | type: 'image/png', 48 | label: 'Screenshot of Zero-config PWA Framework-agnostic Plugin for Vite and Integrations', 49 | }], 50 | shortcuts: [{ 51 | name: 'Getting Started', 52 | description: 'Concepts and useful links for PWA (Progressive web applications): PWA made easy', 53 | url: '/guide/', 54 | icons: [{ 55 | src: 'shortcuts/guide.png', 56 | sizes: '96x96', 57 | type: 'image/png', 58 | }], 59 | }, { 60 | name: 'Assets Generator', 61 | description: 'Generate all the PWA assets from a single command and a single source image', 62 | url: '/assets-generator/', 63 | icons: [{ 64 | src: 'shortcuts/assets.png', 65 | sizes: '96x96', 66 | type: 'image/png', 67 | }], 68 | }, { 69 | name: 'Frameworks', 70 | description: 'All modern frameworks are supported: VanillaJS, TypeScript, Vue, React, Preact, Svelte, Solid, Web Components', 71 | url: '/frameworks/', 72 | icons: [{ 73 | src: 'shortcuts/frameworks.png', 74 | sizes: '96x96', 75 | type: 'image/png', 76 | }], 77 | }, { 78 | name: 'Deploy', 79 | description: 'Hints about how to deploy your PWA', 80 | url: '/deployment/', 81 | icons: [{ 82 | src: 'shortcuts/deploy.png', 83 | sizes: '96x96', 84 | type: 'image/png', 85 | }], 86 | }, { 87 | name: 'Workbox', 88 | description: 'Production-ready service worker libraries and tooling.', 89 | url: '/workbox/', 90 | icons: [{ 91 | src: 'shortcuts/workbox.png', 92 | sizes: '96x96', 93 | type: 'image/png', 94 | }], 95 | }], 96 | handle_links: 'preferred', 97 | launch_handler: { 98 | client_mode: ['navigate-existing', 'auto'], 99 | }, 100 | edge_side_panel: { 101 | preferred_width: 480, 102 | }, 103 | }, 104 | experimental: { 105 | includeAllowlist: true, 106 | }, 107 | workbox: { 108 | globPatterns: ['**/*.{css,js,html,svg,png,ico,txt,woff2,json,excalidraw}'], 109 | globIgnores: ['shortcuts/*.svg'], 110 | runtimeCaching: [ 111 | { 112 | urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i, 113 | handler: 'CacheFirst', 114 | options: { 115 | cacheName: 'google-fonts-cache', 116 | expiration: { 117 | maxEntries: 10, 118 | maxAgeSeconds: 60 * 60 * 24 * 365, // <== 365 days 119 | }, 120 | cacheableResponse: { 121 | statuses: [0, 200], 122 | }, 123 | }, 124 | }, 125 | { 126 | urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i, 127 | handler: 'CacheFirst', 128 | options: { 129 | cacheName: 'gstatic-fonts-cache', 130 | expiration: { 131 | maxEntries: 10, 132 | maxAgeSeconds: 60 * 60 * 24 * 365, // <== 365 days 133 | }, 134 | cacheableResponse: { 135 | statuses: [0, 200], 136 | }, 137 | }, 138 | }, 139 | { 140 | urlPattern: /^https:\/\/cdn\.jsdelivr\.net\/.*/i, 141 | handler: 'NetworkFirst', 142 | options: { 143 | cacheName: 'jsdelivr-images-cache', 144 | expiration: { 145 | maxEntries: 10, 146 | maxAgeSeconds: 60 * 60 * 24 * 7, // <== 7 days 147 | }, 148 | cacheableResponse: { 149 | statuses: [0, 200], 150 | }, 151 | }, 152 | }, 153 | ], 154 | }, 155 | } 156 | -------------------------------------------------------------------------------- /.vitepress/scripts/transformHead.ts: -------------------------------------------------------------------------------- 1 | import type { HeadConfig, TransformContext } from 'vitepress' 2 | 3 | const firaFont = 'https://fonts.googleapis.com/css2?family=Fira+Code&display=swap' 4 | const googleapis = 'https://fonts.googleapis.com' 5 | const gstatic = 'https://fonts.gstatic.com' 6 | const sponsors = 'https://cdn.jsdelivr.net' 7 | 8 | export async function transformHead({ pageData }: TransformContext): Promise { 9 | const head: HeadConfig[] = [] 10 | 11 | // prefetch fira font 12 | head.push(['link', { rel: 'dns-prefetch', href: googleapis }]) 13 | head.push(['link', { rel: 'dns-prefetch', href: gstatic }]) 14 | head.push(['link', { rel: 'preconnect', crossorigin: 'anonymous', href: googleapis }]) 15 | head.push(['link', { rel: 'preconnect', crossorigin: 'anonymous', href: gstatic }]) 16 | 17 | // non-blocking css 18 | head.push(['link', { rel: 'preload', as: 'style', onload: 'this.onload=null;this.rel=\'stylesheet\'', href: firaFont }]) 19 | head.push(['noscript', {}, ``]) 20 | 21 | // logo images 22 | head.push(['link', { rel: 'prefetch', href: '/icon_light.svg' }]) 23 | head.push(['link', { rel: 'prefetch', href: '/icon_dark.svg' }]) 24 | 25 | // banner for index page 26 | if (pageData.relativePath === 'index.md') { 27 | head.push(['link', { rel: 'dns-prefetch', href: sponsors }]) 28 | head.push(['link', { rel: 'preconnect', crossorigin: 'anonymous', href: sponsors }]) 29 | head.push(['link', { rel: 'prefetch', href: '/banner_light.svg' }]) 30 | head.push(['link', { rel: 'prefetch', href: '/banner_dark.svg' }]) 31 | } 32 | 33 | // prompt for update image 34 | if (pageData.relativePath === 'guide/prompt-for-update.md') 35 | head.push(['link', { rel: 'prefetch', href: '/prompt-update.png' }]) 36 | 37 | return head 38 | } 39 | -------------------------------------------------------------------------------- /.vitepress/theme/PwaLayout.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 55 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ChangeLog.md: -------------------------------------------------------------------------------- 1 | ::: info 2 | Check [change log page for more info](/guide/change-log.html). 3 | ::: 4 | -------------------------------------------------------------------------------- /.vitepress/theme/components/CleanupOutdatedCaches.md: -------------------------------------------------------------------------------- 1 | The service worker will store all your application assets in a browser cache (or set of caches). Every time you make changes to your application and rebuild it, the `service worker` will also be rebuilt, including in its precache manifest all new modified assets, which will have their revision changed (all assets that have been modified will have a new version). Assets that have not been modified will also be included in the service worker precache manifest, but their revision will not change from the previous one. 2 | 3 | ::: tip Precache Manifest Entry Revision 4 | The precache manifest entry revision is just a `MD5` hash of the asset content, if an asset is not modified, the calculated hash will be always the same. 5 | ::: 6 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ExamplesBehaviors.md: -------------------------------------------------------------------------------- 1 | - `Prompt for update`: 2 | - Show `Ready to work offline` on first visit and once the `service worker` ready. 3 | - Show `Prompt for update` when new `service worker` available. 4 | 5 | - `Auto update`: 6 | - Show `Ready to work offline` on first visit and once the `service worker` ready. 7 | - When new content available, the service worker will be updated automatically. 8 | 9 | - `Prompt for update` with `Periodic service worker updates`: 10 | - Show `Ready to work offline` on first visit and once the `service worker` ready. 11 | - Show `Prompt for update` when new `service worker` available. 12 | - The example project will register a `Periodic service worker updates` 13 | 14 | - `Auto update` with `Periodic service worker updates`: 15 | - Show `Ready to work offline` on first visit and once the `service worker` ready. 16 | - The example project will register a `Periodic service worker updates` 17 | - When new content available, the service worker will be updated automatically. 18 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ExamplesGenerateSW.md: -------------------------------------------------------------------------------- 1 | `generateSW` has the following behaviors: 2 | 3 | 4 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ExamplesInjectManifest.md: -------------------------------------------------------------------------------- 1 | `injectManifest` has the following behavior: 2 | 3 | 4 | -------------------------------------------------------------------------------- /.vitepress/theme/components/GenerateSWCleanupOutdatedCaches.md: -------------------------------------------------------------------------------- 1 | When the browser detects and installs the new version of your application, it will have in the cache storage all new assets and also the old ones. To delete old assets (from previous versions that are no longer necessary), you have to configure an option in the `workbox` entry of the plugin configuration. 2 | 3 | When using the `generateSW` strategy, it is not necessary to configure it, the plugin will activate it by default. 4 | 5 | We strongly recommend you to **NOT** deactivate the option. If you are curious, you can deactivate it using the following code in your plugin configuration: 6 | 7 | ```ts 8 | import { VitePWA } from 'vite-plugin-pwa' 9 | 10 | export default defineConfig({ 11 | plugins: [ 12 | VitePWA({ 13 | workbox: { 14 | cleanupOutdatedCaches: false 15 | } 16 | }) 17 | ] 18 | }) 19 | ``` 20 | -------------------------------------------------------------------------------- /.vitepress/theme/components/GenerateSWSourceMap.md: -------------------------------------------------------------------------------- 1 | Since plugin version `0.11.2`, your service worker's source map will not be generated as it uses the `build.sourcemap` option from the Vite config, which by default is `false`. 2 | 3 | Your service worker source map will be generated when Vite's `build.sourcemap` configuration option has the value `true`, `'inline'` or `'hidden'`, and you have not configured the `workbox.sourcemap` option in the plugin configuration. If you configure the `workbox.sourcemap` option, the plugin will not change that value. 4 | 5 | If you want to generate the source map of your service worker, you can use this code: 6 | 7 | ```ts 8 | import { VitePWA } from 'vite-plugin-pwa' 9 | 10 | export default defineConfig({ 11 | plugins: [ 12 | VitePWA({ 13 | workbox: { 14 | sourcemap: true 15 | } 16 | }) 17 | ] 18 | }) 19 | ``` 20 | -------------------------------------------------------------------------------- /.vitepress/theme/components/HeuristicWorkboxWindow.md: -------------------------------------------------------------------------------- 1 | ::: warning 2 | **This only applies when importing any of the virtual modules or using `workbox-window` module**. 3 | 4 | Since `workbox-window` uses a time-based `heuristic` algorithm to handle service worker updates, if you build your service worker and register it again, if the time between last registration and the new one is less than 1 minute, then, `workbox-window` will handle the `service worker update found` event as an external event, and so the behavior could be strange (for example, if using `prompt`, instead showing the dialog for new content available, the ready to work offline dialog will be shown; if using `autoUpdate`, the ready to work offline dialog will be shown and shouldn't be shown). 5 | ::: 6 | -------------------------------------------------------------------------------- /.vitepress/theme/components/HomePage.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 53 | 54 | 63 | -------------------------------------------------------------------------------- /.vitepress/theme/components/InjectManifestBuild.md: -------------------------------------------------------------------------------- 1 | From `v0.18.0`, `vite-plugin-pwa` adds five new options to `injectManifest` option to allow customizing the service worker build output: 2 | - `target`: you can change the `target` build, the plugin will use the Vite's [build.target](https://vitejs.dev/config/build-options.html#build-target) option if not configured 3 | - `minify`: you can change the `minify` build, the plugin will use the Vite's [build.minify](https://vitejs.dev/config/build-options.html#build-minify) option if not configured 4 | - `sourcemap`: you can change the `sourcemap` build, the plugin will use the Vite's [build.sourcemap](https://vitejs.dev/config/build-options.html#build-sourcemap) option if not configured 5 | - `enableWorkboxModulesLogs`: you can enable/disable the `workbox` modules log for a development or production build, by default, the plugin will use `process.env.NODE_ENV` (Workbox modules logs logic will be removed from the service worker in `production` build: dead code elimination) 6 | - `buildPlugins`: you can add custom Rollup and/or Vite plugins to the service worker build 7 | 8 | The new Vite build will allow you to use [.env Files](https://vitejs.dev/guide/env-and-mode.html#env-files), the `mode` option in your PWA configuration will not be used when using `injectManifest` strategy, the plugin will use the Vite's [mode](https://vitejs.dev/config/#mode) option instead: 9 | - use `import.meta.env.MODE` to access the Vite mode inside your service worker. 10 | - use `import.meta.env.DEV` or `import.meta.env.PROD` to check if the service worker is running on development or production (equivalent to `process.env.NODE_ENV`), check Vite [NODE_ENV and Modes](https://vitejs.dev/guide/env-and-mode#node-env-and-modes) docs. 11 | 12 | ::: tip 13 | If you are using TypeScript in your service worker accessing `import.meta.env` variables, if TypeScript complains, add the following reference to the beginning of your service worker code: 14 | ```ts 15 | /// 16 | ``` 17 | ::: 18 | -------------------------------------------------------------------------------- /.vitepress/theme/components/InjectManifestCleanupOutdatedCaches.md: -------------------------------------------------------------------------------- 1 | When the user installs the new version of the application, we will have on the service worker cache all new assets and also the old ones. To delete old assets (from previous versions that are no longer necessary), and since you are building your own service worker, you will need to add the following code to your custom service worker: 2 | 3 | ```js 4 | import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching' 5 | 6 | cleanupOutdatedCaches() 7 | 8 | precacheAndRoute(self.__WB_MANIFEST) 9 | ``` 10 | 11 | We strongly recommend you to include previous code on your custom service worker. 12 | -------------------------------------------------------------------------------- /.vitepress/theme/components/InjectManifestSourceMap.md: -------------------------------------------------------------------------------- 1 | ::: info 2 | From `v0.18.0+` you can use `minify`, `sourcemap` and `enableWorkboxModulesLogs` in your `injectManifest` configuration option, check [New Vite Build](/guide/change-log#new-vite-build) section for more details. 3 | ::: 4 | 5 | Since you are building your own service worker, this plugin will use Vite's `build.sourcemap` configuration option, which default value is `false`, to generate the source map. 6 | 7 | If you want to generate the source map for your service worker, you will need to generate the source map for the entire application. 8 | -------------------------------------------------------------------------------- /.vitepress/theme/components/PromptForUpdateImg.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ReactReactiveWarning.md: -------------------------------------------------------------------------------- 1 | ::: warning 2 | The options provided to hooks are not reactive. Therefore, the callback references will be the first rendered options instead of the latest hook’s options. If you are doing complex logic with state changes, you will need to provide a stable reference function. 3 | ::: 4 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ReloadPrompt.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 51 | -------------------------------------------------------------------------------- /.vitepress/theme/components/RunExamples.md: -------------------------------------------------------------------------------- 1 | ::: warning 2 | Before following the instructions below, read the [Contribution Guide](https://github.com/antfu/vite-plugin-pwa/blob/main/CONTRIBUTING.md). 3 | ::: 4 | 5 | Make sure you install project dependencies, and build the repo on your local clone/fork: 6 | 7 | ```shell 8 | cd vite-plugin-pwa 9 | pnpm install 10 | pnpm run build 11 | ``` 12 | 13 | To run the examples, execute the following script from your shell (from root folder), it will start a CLI where you will select the framework and the pwa options: 14 | 15 | ```shell 16 | pnpm run examples 17 | ``` 18 | 19 | If you don't run `pnpm build` first, you may see an error like, `failed to load config` or `Please verify that the package.json has a valid "main" entry`. 20 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ScaffoldingPWAProject.md: -------------------------------------------------------------------------------- 1 | ::: tip Compatibility Note 2 | Vite requires [Node.js](https://nodejs.org/en/) version 18.x.x or 20+. However, some templates may require a higher Node.js version to work, please upgrade Node if your package manager warns about it. 3 | ::: 4 | 5 | ::: code-group 6 | 7 | ```bash [pnpm] 8 | $ pnpm create @vite-pwa/pwa 9 | ``` 10 | 11 | ```bash [yarn] 12 | $ yarn create @vite-pwa/pwa 13 | ``` 14 | 15 | ```bash [npm] 16 | $ npm create @vite-pwa/pwa@latest 17 | ``` 18 | 19 | ```bash [bun] 20 | $ bun create @vite-pwa/pwa 21 | ``` 22 | 23 | ::: 24 | 25 | Then follow the prompts! 26 | 27 | You can also directly specify the project name and the template you want to use via additional command line options. For example, to scaffold a Vite PWA + Vue project, run: 28 | 29 | ::: code-group 30 | 31 | ```bash [pnpm] 32 | $ pnpm create @vite-pwa/pwa my-vue-app --template vue 33 | ``` 34 | 35 | ```bash [yarn] 36 | $ yarn create @vite-pwa/pwa my-vue-app --template vue 37 | ``` 38 | 39 | ```bash [npm] 40 | $ npm create @vite-pwa/pwa@latest my-vue-app -- --template vue 41 | ``` 42 | 43 | ```bash [bun] 44 | $ bun create @vite-pwa/pwa my-vue-app --template vue 45 | ``` 46 | 47 | ::: 48 | 49 | See [create-pwa](https://github.com/vite-pwa/create-pwa) for more details on each supported template: `vanilla`, `vanilla-ts`, `vue`, `vue-ts`, `react`, `react-ts`, `preact`, `preact-ts`, `lit`, `lit-ts`, `svelte`, `svelte-ts`, `solid`, `solid-ts` (templates can be found inside the `templates` folder). 50 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ServiceWorkerClientErrors.md: -------------------------------------------------------------------------------- 1 | Check [New Vite Build](/guide/change-log#new-vite-build) section for more details, the error described below has been fixed in `v0.18.0+` and there is no need to use `iife` format to build your service worker. 2 | 3 | If your service worker code is being compiled with unexpected `exports` (for example: `export default require_sw();`), you can change the build output format to `iife`, add the following code to your pwa configuration: 4 | 5 | ```ts 6 | injectManifest: { 7 | rollupFormat: 'iife' 8 | } 9 | ``` 10 | 11 | -------------------------------------------------------------------------------- /.vitepress/theme/components/SsrSsg.md: -------------------------------------------------------------------------------- 1 | If you are using `SSR/SSG`, you need to import `virtual:pwa-register` module using dynamic import and checking if `window` is not `undefined`. 2 | 3 | You can register the service worker on `src/pwa.ts` module: 4 | 5 | ```ts 6 | import { registerSW } from 'virtual:pwa-register' 7 | 8 | registerSW({ /* ... */ }) 9 | ``` 10 | 11 | and then import it from your `main.ts`: 12 | 13 | ```ts 14 | if (typeof window !== 'undefined') 15 | import('./pwa') 16 | ``` 17 | 18 | You can see the [FAQ](/guide/faq#navigator-window-is-undefined) entry for more info. 19 | -------------------------------------------------------------------------------- /.vitepress/theme/components/TypeScriptError2307.md: -------------------------------------------------------------------------------- 1 | If your **TypeScript** build step or **IDE** complain about not being able to find modules or type definitions on imports, add the following to the `compilerOptions.types` array of your `tsconfig.json`: 2 | 3 | ```json 4 | { 5 | "compilerOptions": { 6 | "types": [ 7 | "vite-plugin-pwa/client" 8 | ] 9 | } 10 | } 11 | ``` 12 | 13 | Or you can add the following reference in any of your `d.ts` files (for example, in `vite-env.d.ts` or `global.d.ts`): 14 | ```ts 15 | /// 16 | ``` 17 | -------------------------------------------------------------------------------- /.vitepress/theme/components/vp/NavBarTitle.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | 19 | 57 | -------------------------------------------------------------------------------- /.vitepress/theme/components/vp/TeamMembersItem.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 57 | 58 | 231 | -------------------------------------------------------------------------------- /.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue' 2 | import type { Theme } from 'vitepress' 3 | import DefaultTheme from 'vitepress/theme' 4 | import PwaLayout from './PwaLayout.vue' 5 | 6 | import './styles/main.css' 7 | import './styles/vars.css' 8 | 9 | import 'uno.css' 10 | import 'virtual:group-icons.css' 11 | 12 | export default { 13 | extends: DefaultTheme, 14 | Layout() { 15 | return h(PwaLayout) 16 | }, 17 | } satisfies Theme 18 | -------------------------------------------------------------------------------- /.vitepress/theme/styles/vars.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Colors 3 | * -------------------------------------------------------------------------- */ 4 | :root { 5 | --vp-c-brand-1: #00586e; 6 | --vp-c-brand-2: #2E859C; 7 | --vp-c-brand-3: #2c7f95; 8 | --vp-c-warning-1: #827717; 9 | --vp-c-warning-soft: #eeeef0; 10 | --vp-c-danger-1: #b71c1c; 11 | --vp-c-danger-soft: #ffebee77; 12 | --vp-c-sponsor: #d51a6d; 13 | /* pwa prompt divider */ 14 | --pwa-border: #e5e5e5; 15 | } 16 | .dark { 17 | --vp-c-brand-1: #4ba8c2; 18 | --vp-c-brand-2: #52b1cc; 19 | --vp-c-warning-1: #EAB306; 20 | --vp-c-danger-1: #fff; 21 | --vp-c-danger-soft: #660000; 22 | --vp-c-sponsor: #ff3d93; 23 | /* pwa prompt divider */ 24 | --pwa-border: #ffffff75; 25 | } 26 | 27 | /** 28 | * Component: Custom Block 29 | * -------------------------------------------------------------------------- */ 30 | :root { 31 | --vp-custom-block-info-border: var(--vp-c-border); 32 | --vp-custom-block-info-text: var(--vp-c-text-2); 33 | --vp-custom-block-info-bg: var(--vp-c-default-soft); 34 | --vp-custom-block-info-code-bg: var(--vp-c-default-soft); 35 | 36 | --vp-custom-block-tip-border: var(--vp-c-brand-1); 37 | --vp-custom-block-tip-text: var(--vp-c-brand-1); 38 | --vp-custom-block-tip-bg: tranparent; 39 | --vp-custom-block-tip-code-bg: var(--vp-c-default-soft); 40 | 41 | --vp-custom-block-warning-border: var(--vp-c-warning-1); 42 | --vp-custom-block-warning-text: var(--vp-c-warning-1); 43 | --vp-custom-block-warning-bg: #ffffe777; 44 | --vp-custom-block-warning-code-bg: var(--vp-c-warning-soft); 45 | 46 | --vp-custom-block-danger-border: var(--vp-c-danger-1); 47 | --vp-custom-block-danger-text: var(--vp-c-danger-1); 48 | --vp-custom-block-danger-bg: var(--vp-c-danger-soft); 49 | --vp-custom-block-danger-code-bg: #eeeef0; 50 | } 51 | 52 | .dark { 53 | --vp-custom-block-warning-border: #827717; 54 | --vp-custom-block-warning-text: #EAB306; 55 | --vp-custom-block-warning-bg: #323238; 56 | --vp-custom-block-warning-code-bg: #323238; 57 | 58 | --vp-custom-block-danger-code-bg: #323238; 59 | } 60 | 61 | /** 62 | * Component: Home 63 | * -------------------------------------------------------------------------- */ 64 | 65 | :root { 66 | --vp-home-hero-name-color: transparent; 67 | --vp-home-hero-name-background: linear-gradient(347deg, #358399 30%, #3bb89c); 68 | --vp-home-hero-image-background-image: linear-gradient(45deg, #00586e60 30%, #e5e05660); 69 | --vp-home-hero-image-filter: blur(30px); 70 | } 71 | 72 | @media (min-width: 640px) { 73 | :root { 74 | --vp-home-hero-image-filter: blur(56px); 75 | } 76 | } 77 | 78 | @media (min-width: 960px) { 79 | :root { 80 | --vp-home-hero-image-filter: blur(72px); 81 | } 82 | } 83 | 84 | /** 85 | * Component: Algolia 86 | * -------------------------------------------------------------------------- */ 87 | 88 | .DocSearch { 89 | --docsearch-primary-color: var(--vp-c-brand) !important; 90 | } 91 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Hi! We are really excited that you are interested in contributing to `vite-pwa-docs`. Before submitting your contribution, please make sure to take a moment and read through the following guide. 4 | 5 | Refer also to https://github.com/antfu/contribute. 6 | 7 | ## Set up your local development environment 8 | 9 | The package manager used to install must be [pnpm](https://pnpm.io/). 10 | 11 | To develop and test the `vite-pwa-docs` package: 12 | 13 | 1. Fork the `vite-pwa-docs` repository to your own GitHub account and then clone it to your local device. 14 | 15 | 2. Ensure using the latest Node.js (16.x) 16 | 17 | 3. `vite-pwa-docs` uses pnpm v8. If you are working on multiple projects with different versions of pnpm, it's recommend to enable [Corepack](https://github.com/nodejs/corepack) by running `corepack enable`. 18 | 19 | 4. Check out a branch where you can work and commit your changes: 20 | ```shell 21 | git checkout -b my-new-branch 22 | ``` 23 | 24 | 5. Run `pnpm i` in `vite-pwa-docs`'s root folder 25 | 26 | ## Testing website docs changes 27 | 28 | `vite-pwa-docs` uses `Vitepress` for the documentation website. 29 | 30 | Once you made the changes to the documentation, you can test them running `nr dev` from the root folder. 31 | 32 | To check the website build, you can: 33 | - run `nr preview` from the root folder. 34 | - run `nr https` from the root folder (to test it using `https`). 35 | 36 | ## Excalidraw diagram and Cookbook page 37 | 38 | Open `public/vite-plugin-pwa.excalidraw` file in [Excalidraw](https://excalidraw.com/). 39 | 40 | Apply your changes in the diagram: all images are grouped, you can remove the group to change the diagram, don't forget to group it once changes are done. 41 | 42 | Save your changes, don't forget to save it to local: `public/vite-plugin-pwa.excalidraw`. 43 | 44 | Save the diagram using `Export link`: copy the link and paste it in the `guide/cookbook.md` file link. 45 | 46 | Export each changed image as SVG: click on the image to select it, click on the `...` button, click on `Export as SVG`, save the file in `assets` folder using `Only Selected` and `Scale 3x` options (uncheck background, dark mode and embed scene if checked). 47 | 48 | Save each image changed to the `assets` folder: 49 | - vite-build-cli.svg 50 | - vite-config-file.svg 51 | - close-bundle-hook.svg 52 | - inject-manifest.svg 53 | 54 | Once all SVG image saved in `assets` folder, you need to remove the `height`, update the `width`, add `preserveAspectRatio` and change the `font-face src` to be local: 55 | - remove `height` attribute 56 | - change `width` attribute to `100%` 57 | - add `preserveAspectRatio` attribute with `xMidYMid meet` value 58 | - change `@font-face src` attribute to `url("/Virgil.woff2");` for and `url("/Cascadia.woff2");` for Virgil and Cascadia fonts in the style tag 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-PRESENT Anthony Fu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Documentation for PWA integrations for Vite and the ecosystem
3 | Documentation: PWA integrations for Vite and the ecosystem 4 |

5 | 6 |

7 | 8 | Documentation & Guides 9 | 10 |
11 | 12 | GitHub stars 13 | 14 |

15 | 16 |
17 | 18 |

19 | 20 | 21 | 22 |

23 | 24 | 25 | ## 🚀 Features 26 | 27 | - 📖 [**Documentation & guides**](https://vite-pwa-org.netlify.app/) 28 | - 👌 **Zero-Config**: sensible built-in default configs for common use cases 29 | - 🔩 **Extensible**: expose the full ability to customize the behavior of the plugin 30 | - 🦾 **Type Strong**: written in [TypeScript](https://www.typescriptlang.org/) 31 | - 🔌 **Offline Support**: generate service worker with offline support (via Workbox) 32 | - ⚡ **Fully tree shakable**: auto inject Web App Manifest 33 | - 💬 **Prompt for new content**: built-in support for Vanilla JavaScript, Vue 3, React, Svelte, SolidJS and Preact 34 | - ⚙️ **Stale-while-revalidate**: automatic reload when new content is available 35 | - ✨ **Static assets handling**: configure static assets for offline support 36 | - 🐞 **Development Support**: debug your custom service worker logic as you develop your application 37 | - 🛠️ **Versatile**: integration with meta frameworks: [îles](https://github.com/ElMassimo/iles), [SvelteKit](https://github.com/sveltejs/kit), [VitePress](https://github.com/vuejs/vitepress), [Astro](https://github.com/withastro/astro), [Nuxt 3](https://github.com/nuxt/nuxt) and [Remix](https://github.com/remix-run/remix) 38 | - 💥 **PWA Assets Generator**: generate all the PWA assets from a single command and a single source image 39 | - 🚀 **PWA Assets Integration**: serving, generating and injecting PWA Assets on the fly in your application 40 | 41 | ## 📦 Integrations 42 | 43 | - [îles](https://github.com/ElMassimo/iles/tree/main/packages/pwa): documentation can be found [here](https://iles-docs.netlify.app/guide/pwa) 44 | - [SvelteKit](https://github.com/vite-pwa/sveltekit) 45 | - [VitePress](https://github.com/vite-pwa/vitepress) 46 | - [Astro](https://github.com/vite-pwa/astro) 47 | - [Nuxt 3](https://github.com/vite-pwa/nuxt) 48 | - [Remix](https://github.com/remix-run/remix) 49 | 50 | ## 📄 License 51 | 52 | [MIT](./LICENSE) License © 2021-PRESENT [Anthony Fu](https://github.com/antfu) 53 | -------------------------------------------------------------------------------- /assets-generator/api.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API | PWA Assets Generator 3 | outline: deep 4 | --- 5 | 6 | # PWA Assets Generator API 7 | 8 | From `v0.2.0`, `@vite-pwa/assets-generator` is shipped with a CLI, an API (low-level api): refer to [API](#api) for more details. 9 | 10 | The API can be found in the [api folder](https://github.com/vite-pwa/assets-generator/tree/main/src/api). 11 | 12 | ## Installation 13 | 14 | This package is shipped with the `@vite-pwa/assets-generator` package: 15 | 16 | ::: code-group 17 | ```bash [pnpm] 18 | pnpm add -D @vite-pwa/assets-generator 19 | ``` 20 | ```bash [yarn] 21 | yarn add -D @vite-pwa/assets-generator 22 | ``` 23 | ```bash [npm] 24 | npm install -D @vite-pwa/assets-generator 25 | ``` 26 | ::: 27 | 28 | ## API 29 | 30 | From version `v0.2.0`, `@vite-pwa/assets-generator` exposes the following packages: 31 | - `@vite-pwa/assets-generator/api`: low-level functions api. 32 | - new `@vite-pwa/assets-generator/api/instructions`: `instructions` function to collect the icon assets instructions. 33 | - new `@vite-pwa/assets-generator/api/generate-assets`: `generateAssets` function to generate icon assets from an instruction. 34 | - new `@vite-pwa/assets-generator/api/generate-html-markup`: `generateHtmlMarkup` function to generate all html head links from an instruction. 35 | - new `@vite-pwa/assets-generator/api/generate-manifest-icons-entry`: `generateManifestIconsEntry` function to generate the PWA web manifest icons' entry. 36 | 37 | The API can be found in the [api folder](https://github.com/vite-pwa/assets-generator/tree/main/src/api) and the [JSDOCS documentation](https://paka.dev/npm/@vite-pwa/assets-generator). 38 | 39 | If you need to generate the PWA assets from your own code, you can use the `instructions`, `generateHtmlMarkup`, `generateAssets` and `generateManifestIconsEntry` functions: 40 | 1) [instructions](https://github.com/vite-pwa/assets-generator/tree/main/src/api/instructions.ts): collect the icon assets instructions, provides function helpers to generate each icon asset as a `Buffer` and html head links with `string` and `object` notation. 41 | 2) [generateAssets](https://github.com/vite-pwa/assets-generator/tree/main/src/api/generate-assets.ts): once you collect the icon assets instructions, you can use this function to save all the icon assets to the file system. 42 | 3) [generateHtmlMarkup](https://github.com/vite-pwa/assets-generator/tree/main/src/api/generate-html-markup.ts): once you collect the icon assets instructions, you can use this function to generate all the html head markup for the icon assets. 43 | 4) [generateManifestIconsEntry](https://github.com/vite-pwa/assets-generator/tree/main/src/api/generate-manifest-icons-entry.ts) function to generate the PWA web manifest icons' entry. 44 | 45 | ::: info 46 | We're working to expose the new api in `vite-plugin-pwa` plugin and the integrations. 47 | 48 | From `v0.19.0`, `vite-plugin-pwa` includes a new experimental feature, check [Integrations](/assets-generator/integrations) section. 49 | ::: 50 | 51 | ### v0.1.0 52 | 53 | As mentioned previously, the API is low-level, it means that you have to handle the default values yourself: you can check the default values in the [defaults.ts](https://github.com/vite-pwa/assets-generator/tree/main/src/api/defaults.ts) module. 54 | 55 | The CLI has been rebuilt on top of the API, you can check the [CLI documentation](/assets-generator/cli) for more details about the default values. 56 | 57 | 58 | -------------------------------------------------------------------------------- /assets-generator/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started | PWA Assets Generator 3 | prev: FAQ | Guide 4 | outline: deep 5 | --- 6 | 7 | # Getting Started 8 | 9 | [@vite-pwa/assets-generator](https://github.com/vite-pwa/assets-generator) will generate all the icons required for your PWA application using [sharp](https://github.com/lovell/sharp/) and [sharp-ico](https://github.com/ssnangua/sharp-ico) packages. 10 | 11 | This package has been developed based on the work done in [Elk PWA Icon Generator Script](https://github.com/elk-zone/elk/blob/main/scripts/generate-pwa-icons.ts). 12 | 13 | With a single image source you can generate all the required icons for your PWA application, via `@vite-pwa/assets-generator` [CLI](/assets-generator/cli) or [API](/assets-generator/api). 14 | 15 | ## Source images 16 | 17 | We strongly recommend using SVG images as source images, as they will be resized to the required sizes without losing quality, but should also work with any image type. 18 | 19 | The svg sources can also be used in for the favicon html head link. 20 | 21 | ## PWA Minimal Icons Requirements 22 | 23 | As pointed out in [PWA Minimal Requirements](/guide/pwa-minimal-requirements), you will need: 24 | - a 192x192 icon (PWA Manifest icon) 25 | - a 512x512 icon (PWA Manifest icon) 26 | - a 180x180 icon for iOS/MacOS (html head link: ``) 27 | 28 | We also suggest you to include: 29 | - A 64x64 icon for Windows (Edge) (PWA Manifest icon) 30 | - A 512x512 icon for Android with `purpose: 'any'` (PWA Manifest icon) 31 | - Avoid using `purpose: 'any maskable'` icon, as it is not supported by all browsers 32 | - An `favicon.ico` and `favicon.svg`, check [Preset Minimal 2023](#preset-minimal-2023) for more details 33 | 34 | ### Preset Minimal 2023 35 | 36 | Refer to [Definitive edition of "How to Favicon" in 2023](https://dev.to/masakudamatsu/favicon-nightmare-how-to-maintain-sanity-3al7) for more details. 37 | 38 | Our minimal recommendation is: 39 | - transparent 48x48 ico: register it in the html head: `` 40 | - Use SVG image as source image: register it in the html head: `` 41 | - transparent 64x64 icon (PWA Manifest icon) 42 | - transparent 192x192 icon (PWA Manifest icon) 43 | - transparent 512x512 icon with `purpose: 'any'` (PWA Manifest icon) 44 | - white 512x512 icon with `purpose: 'maskable'` (PWA Manifest icon): background color can be customized to your needs 45 | - white 180x180 icon for iOS/MacOS (html head link: ``): background color can be customized to your needs 46 | 47 | ### Preset Minimal 48 | 49 | Our minimal recommendation is: 50 | - transparent 64x64 ico: register it in the html head: `` 51 | - Use SVG image as source image: register it in the html head: `` 52 | - transparent 64x64 icon (PWA Manifest icon) 53 | - transparent 192x192 icon (PWA Manifest icon) 54 | - transparent 512x512 icon with `purpose: 'any'` (PWA Manifest icon) 55 | - white 512x512 icon with `purpose: 'maskable'` (PWA Manifest icon): background color can be customized to your needs 56 | - white 180x180 icon for iOS/MacOS (html head link: ``): background color can be customized to your needs 57 | 58 | ## Example using minimal preset 59 | 60 | You can generate icons using the `minimal-2023` preset included in [@vite-pwa/assets-generator](https://github.com/vite-pwa/assets-generator) package via a source image, check out the [CLI](/assets-generator/cli) and [API](/assets-generator/api) documentation for more details. 61 | 62 | Update your PWA manifest icons entry with: 63 | ```ts 64 | icons: [ 65 | { 66 | src: 'pwa-64x64.png', 67 | sizes: '64x64', 68 | type: 'image/png' 69 | }, 70 | { 71 | src: 'pwa-192x192.png', 72 | sizes: '192x192', 73 | type: 'image/png' 74 | }, 75 | { 76 | src: 'pwa-512x512.png', 77 | sizes: '512x512', 78 | type: 'image/png', 79 | purpose: 'any' 80 | }, 81 | { 82 | src: 'maskable-icon-512x512.png', 83 | sizes: '512x512', 84 | type: 'image/png', 85 | purpose: 'maskable' 86 | } 87 | ] 88 | ``` 89 | 90 | and use the following HTML head entries in your entry point: 91 | 92 | ### Using Preset Minimal 2023 93 | 94 | ```html 95 | 96 | 97 | 98 | 99 | 100 | ``` 101 | 102 | ### Using Preset Minimal 103 | 104 | ```html 105 | 106 | 107 | 108 | 109 | 110 | ``` 111 | -------------------------------------------------------------------------------- /assets-generator/migrations.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Migrations | PWA Assets Generator 3 | next: Getting Started | Frameworks 4 | outline: deep 5 | --- 6 | 7 | # Migrations 8 | 9 | When migrating from one version to a new one, you should remove all the PWA assets generated previously and generate them again after upgrading `@vite-pwa/assets-generator` package. 10 | 11 | :::warning 12 | If you're using some of the old PWA assets in your application, **don't remove them**. 13 | ::: 14 | 15 | Remember to check the changes before upgrading to a new version in your local environment: 16 | - start the current version in your local server, opening your application to check the old PWA assets 17 | - upgrade the package to the new version and regenerate the PWA assets 18 | - start the new version in your local server, refresh your application to check the new PWA assets 19 | 20 | ## From `v0.1.0` to `v0.2.0` 21 | 22 | The `api` and the core has been built from scratch, the CLI has been rebuilt on top of the API. 23 | 24 | The main changes included in version `v0.2.0` are: 25 | - `generatePWAImageAssets` and `generatePWAAssets` functions have been removed from `@vite-pwa/assets-generator` package export: now the package only export types and some utilities. 26 | - new `@vite-pwa/assets-generator/api/instructions` package export: new `instructions` function to collect the icon assets instructions. 27 | - new `@vite-pwa/assets-generator/api/generate-assets` package export: new `generateAssets` function to generate icon assets from an instruction. 28 | - new `@vite-pwa/assets-generator/api/generate-html-markup` package export: new `generateHtmlMarkup` function to generate all html head links from an instruction. 29 | - new `@vite-pwa/assets-generator/api/generate-manifest-icons-entry` package export: new `generateManifestIconsEntry` function to generate the PWA web manifest icons' entry. 30 | - new CLI options for html head links generation: `xhtml` and `includeId`. 31 | 32 | If you are using `generatePWAImageAssets` and/or `generatePWAAssets` functions, you need to update your code to use the new `instructions` and `generateAssets` functions. 33 | 34 | If you're only using the CLI, you don't need to change anything. 35 | 36 | For more details about the new version `v0.2.0`, check [this comment](https://github.com/vite-pwa/assets-generator/issues/20#issuecomment-1848382903) in the repository. 37 | 38 | ## From `minimal` to `minimal-2023` Preset 39 | 40 | If you are using `pwa-assets-generator` in your `package.json` scripts, update the script from: 41 | ```json 42 | "generate-assets": "pwa-assets-generator --preset minimal " 43 | ``` 44 | to: 45 | ```json 46 | "generate-assets": "pwa-assets-generator --preset minimal-2023 " 47 | ``` 48 | 49 | If you are using a configuration file: 50 | - update the built-in preset name or update the import to use `minimal2023Preset`: check the code snippets in the [built-in features section](/assets-generator/cli#built-in-features). 51 | - include `headLinkOptions.preset = '2023'` in you configuration file 52 | 53 | 54 | The new `minimal-2023` preset changes only the `favicon.ico` size, the `apple-touch-icon` and PWA manifest icons are the same, you need to update your html head favicon entries, from: 55 | ```html 56 | 57 | 58 | 59 | 60 | ``` 61 | 62 | to: 63 | ```html 64 | 65 | 66 | 67 | 68 | ``` 69 | 70 | -------------------------------------------------------------------------------- /avatars/antfu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/avatars/antfu.png -------------------------------------------------------------------------------- /avatars/create-webp.mjs: -------------------------------------------------------------------------------- 1 | import sharp from 'sharp' 2 | import fg from 'fast-glob' 3 | 4 | async function createWebp() { 5 | /** @type {string[]} */ 6 | const images = await fg('./avatars/*.png', { absolute: false }) 7 | for (const image of images) { 8 | await sharp(image).resize({ width: 128, height: 128 }).webp({ quality: 100 }).toFile( 9 | image.replace('./avatars/', './public/team-avatars/').replace('.png', '.webp'), 10 | ) 11 | } 12 | } 13 | 14 | createWebp() 15 | -------------------------------------------------------------------------------- /avatars/hannoeru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/avatars/hannoeru.png -------------------------------------------------------------------------------- /avatars/userquin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/avatars/userquin.png -------------------------------------------------------------------------------- /deployment/apache.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Apache Http Server 2.4+ | Deployment 3 | next: Getting Started | Workbox 4 | --- 5 | 6 | # Apache Http Server 2.4+ 7 | 8 | ## Configure `manifest.webmanifest` mime type 9 | 10 | You need to configure the following mime type (see basic configuration below): 11 | 12 | ```ini 13 | 14 | # Manifest file 15 | AddType application/manifest+json webmanifest 16 | 17 | ``` 18 | 19 | ## Basic configuration with http to https redirection 20 | 21 | Update your `httpd.conf` configuration file with: 22 | 23 | ```ini 24 | # httpd.conf 25 | ServerRoot "" 26 | 27 | Listen 80 28 | ServerName www.yourdomain.com 29 | 30 | DocumentRoot "" 31 | 32 | # modules 33 | LoadModule mime_module modules/mod_mime.so 34 | LoadModule rewrite_module modules/mod_rewrite.so 35 | 36 | # mime types 37 | 38 | # Manifest file 39 | AddType application/manifest+json webmanifest 40 | 41 | 42 | # your https configuration 43 | Include conf/extra/https-www.yourdomain.com.conf 44 | 45 | 46 | SSLRandomSeed startup builtin 47 | SSLRandomSeed connect builtin 48 | 49 | 50 | 51 | ServerName www.yourdomain.com 52 | 53 | RewriteEngine On 54 | 55 | # disable TRACE and TRACK methods 56 | RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) 57 | RewriteRule .* - [F] 58 | 59 | Options +FollowSymlinks 60 | RewriteCond %{SERVER_PORT} !443 61 | 62 | RewriteRule (.*) https://www.yourdomain.com/ [L,R] 63 | 64 | ErrorLog logs/www.yourdomain.com-error_log 65 | CustomLog logs/www.yourdomain.com-access_log combined 66 | 67 | ``` 68 | -------------------------------------------------------------------------------- /deployment/aws.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: AWS Amplify | Deployment 3 | --- 4 | 5 | # AWS Amplify 6 | 7 | ::: info WIP 8 | Will coming soon. 9 | ::: 10 | -------------------------------------------------------------------------------- /deployment/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started | Deploy 3 | prev: Remix | Examples 4 | --- 5 | 6 | # Getting Started 7 | 8 | Since you need to install your application as a [Progressive Web App](https://web.dev/explore/progressive-web-apps), you must configure your server to meet [PWA Minimal Requirements](/guide/pwa-minimal-requirements), that is, your server **must**: 9 | - serve `manifest.webmanifest` with `application/manifest+json` mime type 10 | - you must serve your application over `https` 11 | - you must redirect from `http` to `https` 12 | 13 | ## Cache-Control 14 | 15 | Ensure you have a very restrictive setup for your `Cache-Control` headers in place. 16 | 17 | Double check that **you do not** have caching features enabled, especially `immutable`, on locations like: 18 | - `/` 19 | - `/sw.js` 20 | - `/index.html` 21 | - `/manifest.webmanifest` 22 | 23 | ::: danger 24 | **Always re-test and re-assure** that the caching for mission critical files is **as low** as possible if not hashed files or you might invalidate clients for a long time. 25 | ::: 26 | 27 | ## Servers 28 | 29 | - [Netlify](/deployment/netlify) 30 | - [AWS Amplify](/deployment/aws) 31 | - [Vercel](/deployment/vercel) 32 | - [NGINX](/deployment/nginx) 33 | - [Apache Http Server 2.4+](/deployment/apache) 34 | 35 | 36 | ## Testing your application on production 37 | 38 | Once you deploy your application to your server, you can test it using [WebPageTest](https://www.webpagetest.org/). 39 | 40 | There are many test sites, but we suggest you use `WebPageTest` as this is the most comprehensive in terms of test: 41 | - Security. 42 | - First byte time. 43 | - Keep alive enabled. 44 | - Compress transfer. 45 | - Cache static content. 46 | - Effective use of CDN. 47 | - Lighthouse: Core Web Vitals, Performance, Images size optimization... 48 | - And more... 49 | 50 | Enter the url of your application, click `Start Test` button, wait for the test to finish, the `WebPageTest` result will hint you what things on your application must be fixed/changed. The `WebPageTest` result will also score your application, it will also test your site with `Lighthouse`. 51 | 52 | For example, go to [WebPageTest](https://www.webpagetest.org/), enter `https://vite-pwa-org.netlify.app/`, click `Start Test` button, wait a few seconds for the test to finish, and see the results for this site. 53 | -------------------------------------------------------------------------------- /deployment/netlify.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Netlify | Deployment 3 | --- 4 | 5 | # Netlify 6 | 7 | ## Configure `manifest.webmanifest` mime type 8 | 9 | You need to register the correct MIME type for the web manifest by adding a headers table to your `netlify.toml` file (see basic deployment below): 10 | 11 | ```toml 12 | [[headers]] 13 | for = "/manifest.webmanifest" 14 | [headers.values] 15 | Content-Type = "application/manifest+json" 16 | ``` 17 | 18 | ## Cache-Control 19 | 20 | As a general rule, files in `/assets/` can have a very long cache time, as everything in there should contain a hash in the filename. 21 | 22 | Add another headers table to your `netlify.toml` file (see basic deployment below): 23 | 24 | ```toml 25 | [[headers]] 26 | for = "/assets/*" 27 | [headers.values] 28 | cache-control = ''' 29 | max-age=31536000, 30 | immutable 31 | ''' 32 | ``` 33 | 34 | ## Configure http to https redirection 35 | 36 | Netlify will redirect automatically, so you don't worry about it. 37 | 38 | ## Basic deployment example 39 | 40 | Add `netlify.toml` file to the root directory with the content: 41 | 42 | ```toml 43 | [build] 44 | publish = "dist" 45 | command = "pnpm run build" 46 | 47 | [[redirects]] 48 | from = "/*" 49 | to = "/index.html" 50 | status = 200 51 | 52 | [[headers]] 53 | for = "/manifest.webmanifest" 54 | [headers.values] 55 | Content-Type = "application/manifest+json" 56 | 57 | [[headers]] 58 | for = "/assets/*" 59 | [headers.values] 60 | cache-control = ''' 61 | max-age=31536000, 62 | immutable 63 | ''' 64 | ``` 65 | -------------------------------------------------------------------------------- /deployment/nginx.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: NGINX | Deployment 3 | --- 4 | 5 | # NGINX 6 | 7 | ## Configure `manifest.webmanifest` mime type 8 | 9 | You need to register the correct MIME type for the web manifest by adding it either to the [default](https://www.nginx.com/resources/wiki/start/topics/examples/full/#mime-types) file at `/etc/nginx/mime.types` 10 | 11 | ```nginx 12 | # /etc/nginx/mime.types 13 | types { 14 | # Manifest files 15 | application/manifest+json webmanifest; 16 | ... 17 | } 18 | ``` 19 | 20 | or any `http`, `server` or location `location` block with 21 | 22 | 23 | ```nginx 24 | include mime.types; 25 | types { 26 | application/manifest+json webmanifest; 27 | } 28 | ``` 29 | 30 | You can validate the setting by checking the HTTP headers once the app is deployed 31 | 32 | ```shell script 33 | curl -s -I -X GET https://yourserver/manifest.webmanifest | grep content-type -i 34 | ``` 35 | 36 | and check that the result is `content-type: application/manifest+json`. 37 | 38 | ## Basic configuration with http to https redirection 39 | 40 | Update your `server.conf` configuration file with: 41 | 42 | ```nginx 43 | server { 44 | listen 80; 45 | server_name yourdomain.com www.yourdomain.com; 46 | return 301 https://yourdomain.com$request_uri; 47 | } 48 | ``` 49 | 50 | ## Cache-Control 51 | 52 | Ensure you have a very restrictive setup for your `Cache-Control` headers in place. 53 | 54 | Double check that **you do not** have caching features enabled, especially `immutable`, on locations like: 55 | 56 | - `/` 57 | - `/sw.js` 58 | - `/index.html` 59 | - `/manifest.webmanifest` 60 | 61 | NGINX will add `E-Tag`-headers itself, so there is not much to in that regard. 62 | 63 | As a general rule, files in `/assets/` can have a very long cache time, as everything in there should contain a hash in the filename. 64 | 65 | An example configuration inside your `server` block could be: 66 | 67 | ```nginx 68 | # all assets contain hash in filename, cache forever 69 | location ^~ /assets/ { 70 | add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable"; 71 | ... 72 | try_files $uri =404; 73 | } 74 | 75 | # all workbox scripts are compiled with hash in filename, cache forever 76 | location ^~ /workbox- { 77 | add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable"; 78 | ... 79 | try_files $uri =404; 80 | } 81 | 82 | # assume that everything else is handled by the application router, by injecting the index.html. 83 | location / { 84 | autoindex off; 85 | expires off; 86 | add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always; 87 | ... 88 | try_files $uri /index.html =404; 89 | } 90 | ``` 91 | 92 | Be aware that this is a very simplistic approach and you must test every change, as the NGINX match precedences for locations are not very intuitive and error prone if you do not know the [exact rules](https://docs.nginx.com/nginx/admin-guide/web-server/web-server/#location_priority). 93 | 94 | ::: danger 95 | **Always re-test and re-assure** that the caching for mission critical files is **as low** as possible if not hashed files or you might invalidate clients for a long time. 96 | ::: 97 | -------------------------------------------------------------------------------- /examples/astro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Astro | Examples 3 | --- 4 | 5 | # Astro 6 | 7 | **NOTE**: when running StackBlitz playground, you will need to stop the dev server once started and then run `npm run build && npm run preview` to see the PWA in action. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | 34 | 35 | 36 |
ExampleSourcePlayground
Auto Update PWAGitHub 21 | 22 | Open in StackBlitz 23 | 24 |
Prompt for Update PWAGitHub 30 | 31 | Open in StackBlitz 32 | 33 |
37 | -------------------------------------------------------------------------------- /examples/iles.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: îles | Examples 3 | --- 4 | 5 | # îles 6 | 7 | You can test `îles` using the source code from its documentation website, you can find it under [docs](https://github.com/ElMassimo/iles/tree/main/docs) package/directory. 8 | 9 | The behavior used in this website is [Prompt for update](/guide/prompt-for-update). 10 | -------------------------------------------------------------------------------- /examples/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started | Examples 3 | prev: Remix | Frameworks 4 | --- 5 | 6 | # Getting Started 7 | 8 | You can find a set of examples projects on [Vite Plugin PWA GitHub repo](https://github.com/antfu/vite-plugin-pwa/tree/main/examples). 9 | 10 | All the examples projects are under `examples` package/directory of the repo root directory. 11 | 12 | ::: info 13 | The main purpose of these examples projects is to test the service worker and not to meet the [PWA Minimal Requirements](/guide/pwa-minimal-requirements), that is, if you use any of these examples for your projects, you will need to modify the code supplied and then test that it meets the [PWA Minimal Requirements](/guide/pwa-minimal-requirements). Almost all the examples projects should meet [PWA Minimal Requirements](/guide/pwa-minimal-requirements), but you must check it on your target project. 14 | 15 | All the examples projects use `@rollup/plugin-replace` to configure a timestamp initialized to `now` on each build, and so, the service worker will be regenerated/versioned on each build: this timestamp will help us since the service worker won't be regenerated/versioned if none source code changed (on your project you shouldn't want this behavior, you should want to only regenerate/version the service worker when your source code change). 16 | ::: 17 | 18 | ::: warning TRY TO AVOID INCLUDING AUTOMATIC TIMESTAMP ON YOU APPLICATION IF YOU DON'T CHANGE YOUR CODE 19 | We use the timestamp in the examples projects to avoid having to touch a file each time we need to test: for example, to test `Prompt for update`, we need to install the service worker first time (first build), then rebuild and restart the example project and finally refresh the browser to check the `Prompt for update` is shown. 20 | ::: 21 | 22 | ## How to run examples projects? 23 | 24 | If you want to run any of the examples projects you will need to download/clone to your local machine the `Vite Plugin PWA GitHub repo`. 25 | 26 | You will need `node 14` (or newer) to be able to build the `Vite Plugin PWA`. 27 | 28 | ::: warning 29 | Before following the instructions below, read the [Contribution Guide](https://github.com/antfu/vite-plugin-pwa/blob/main/CONTRIBUTING.md). 30 | ::: 31 | 32 | If you don't have installed `PNPM`, you must install it globally via `npm`: 33 | ```shell 34 | npm install -g pnpm 35 | ``` 36 | 37 | Once the repo is on your local machine, you must install project dependencies and build the `vite-plugin-pwa` plugin, just run (from `vite-plugin-pwa` directory cloned locally): 38 | 39 | ```shell 40 | pnpm install 41 | pnpm run build 42 | ``` 43 | 44 | We use `PNPM` but should work with any `package manager`, for example, with `YARN`: 45 | ```shell 46 | yarn && yarn build 47 | ``` 48 | 49 | ::: info 50 | From here on, we will only show the commands to run the examples projects using `PNPM`, we leave it to you how to execute them with any other` package manager`. 51 | ::: 52 | 53 | Before we start running the examples projects, you should consider the following: 54 | - Use `Chromium based` browser: `Chrome`, `Chromium` or `Edge` 55 | - All the examples that are executed in this guide will be done over https, that is, all the projects will respond at address `https://localhost` 56 | - When testing an example project, the `service worker` will be installed in `https://localhost`, and so, subsequent tests in another examples projects may interfere with the previous test, because the `service worker` of the previous project will keep installed on the browser 57 | - Tests should be done on a private window, and so, browser addons/plugins will not interfere with the test 58 | 59 | To avoid `service worker` interference, you should do the following tasks when switching between examples projects: 60 | - Open `dev tools` (`Option + ⌘ + J` on `macOS`, `Shift + CTRL + J` on `Windows/Linux`) 61 | - Go to `Application > Storage`, you should check following checkboxes: 62 | - Application: [x] Unregister service worker 63 | - Storage: [x] Local and session storage 64 | - Cache: [x] Cache storage and [x] Application cache 65 | - Click on `Clear site data` button 66 | - Go to `Application > Service Workers` and check the current `service worker` is missing or has the state `deleted` 67 | 68 | Once we remove the `service worker`, run the corresponding script and just press browser `Refresh` button (or enter `https://localhost` on browser address). 69 | 70 | ## How to test the examples projects Offline? 71 | 72 | To test any of the examples projects (or your project) on `offline`, just open `dev tools` (`Option + ⌘ + J` on `macOS`, `Shift + CTRL + J` on `Windows/Linux`) and go to `Application > Network`, then locate `No throttling` selector: open it and select `Offline` option. 73 | 74 | A common pitfall is to select `Offline` option, then restart the example project (or your project), and refresh the page. In that case, you will have unexpected behavior, and you should remove the service worker. 75 | 76 | If you click the browser `Refresh` button, you can inspect `Application > Network` tab on `dev tools` to check that the `Service Worker` is serving all assets instead request them to the server. 77 | 78 | ::: danger 79 | Don't do a `hard refresh` since it will force the browser to go to the server, and then you will get `No internet connection` page. 80 | ::: 81 | 82 | ## Available Examples Projects 83 | 84 | 85 | 86 | We provide the following examples projects: 87 | - [Vue 3](/examples/vue) 88 | - [Vue 3 generateSW Router Examples](/examples/vue#generatesw): set of examples with disparate behaviors. 89 | - [Vue 3 injectManifest Router Examples](/examples/vue#generatesw): set of examples with disparate behaviors. 90 | - [React](/examples/react) 91 | - [React generateSW Router Examples](/examples/react#generatesw): set of examples with disparate behaviors. 92 | - [React injectManifest Router Examples](/examples/react#generatesw): set of examples with disparate behaviors. 93 | - [Svelte](/examples/svelte) 94 | - [Svelte generateSW Router Examples](/examples/svelte#generatesw): set of examples with disparate behaviors. 95 | - [Svelte injectManifest Router Examples](/examples/svelte#generatesw): set of examples with disparate behaviors. 96 | - [SvelteKit](/examples/sveltekit) 97 | - [SolidJS](/examples/solidjs) 98 | - [SolidJS generateSW Router Examples](/examples/solidjs#generatesw): set of examples with disparate behaviors. 99 | - [SolidJS injectManifest Router Examples](/examples/solidjs#generatesw): set of examples with disparate behaviors. 100 | - [Preact](/examples/preact) 101 | - [Preact generateSW Router Examples](/examples/preact#generatesw): set of examples with disparate behaviors. 102 | - [Preact injectManifest Router Examples](/examples/preact#generatesw): set of examples with disparate behaviors. 103 | - [VitePress](/examples/vitepress). 104 | - [îles](/examples/iles): prompt for update. 105 | - [Astro](/examples/astro). 106 | 107 | -------------------------------------------------------------------------------- /examples/nuxt.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Nuxt 3 | Examples 3 | --- 4 | 5 | # Nuxt 3 6 | 7 | You need to stop the dev server once started and then to see the PWA in action run: 8 | - `nr dev:preview:build`: Nuxt build command + start server 9 | - `nr dev:preview:generate`: Nuxt generate command + start server 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 30 | 31 | 32 |
ExampleSourcePlayground
Auto Update PWA 23 | GitHub 24 | 26 | 27 | Open in StackBlitz 28 | 29 |
33 | 34 | ::: info WIP 35 | You can also check [Elk repo](https://github.com/elk-zone/elk) for a real world example: we're working to update the repo. 36 | 37 | Elk repo is using `Push Notifications` and `Web Share Target API` PWA capabilities and `Prompt for update` register type via `injectManifest` strategy. 38 | ::: 39 | -------------------------------------------------------------------------------- /examples/preact.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Preact | Examples 3 | --- 4 | 5 | # Preact 6 | 7 | The `Preact` example project can be found on [examples/preact-router](https://github.com/vite-pwa/vite-plugin-pwa/tree/main/examples/preact-router) package/directory. 8 | 9 | The router used on this example project is [preact-router](https://github.com/preactjs/preact-router). 10 | 11 | To test `new content available`, you should rerun the corresponding script, and then refresh the page. 12 | 13 | If you are running an example with `Periodic SW updates`, you will need to wait 1 minute: 14 | 15 | 16 | ## Executing the examples 17 | 18 | 19 | 20 | ## generateSW 21 | 22 | 23 | 24 | ## injectManifest 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/qwik.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Qwik | Examples 3 | --- 4 | 5 | # Qwik 6 | 7 | Check the [@qwikdev/pwa](https://github.com/QwikDev/pwa) repository for more details, it is still in its early stages. 8 | 9 | This repository is not using `vite-plugin-pwa` directly (maybe in a future), but it is using Workbox. 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/react.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: React | Examples 3 | --- 4 | 5 | # React 6 | 7 | The `React` example project can be found on [examples/react-router](https://github.com/vite-pwa/vite-plugin-pwa/tree/main/examples/react-router) package/directory. 8 | 9 | The router used on this example project is [react-router](https://reactrouter.com/). 10 | 11 | To test `new content available`, you should rerun the corresponding script, and then refresh the page. 12 | 13 | If you are running an example with `Periodic SW updates`, you will need to wait 1 minute: 14 | 15 | 16 | ## Executing the examples 17 | 18 | 19 | 20 | ## generateSW 21 | 22 | 23 | 24 | ## injectManifest 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/remix.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Remix | Examples 3 | next: Getting Started | Deploy 4 | --- 5 | 6 | # Remix 7 | 8 | You can find a set of examples in the [@vite-pwa/remix integration repo](https://github.com/vite-pwa/remix/tree/main/examples). 9 | -------------------------------------------------------------------------------- /examples/solidjs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SolidJS | Examples 3 | --- 4 | 5 | # SolidJS 6 | 7 | The `SolidJS` example project can be found on [examples/solid-router](https://github.com/vite-pwa/vite-plugin-pwa/tree/main/examples/solid-router) package/directory. 8 | 9 | The router used on this example project is [solid-app-router](https://github.com/solidjs/solid-app-router). 10 | 11 | To test `new content available`, you should rerun the corresponding script, and then refresh the page. 12 | 13 | If you are running an example with `Periodic SW updates`, you will need to wait 1 minute: 14 | 15 | 16 | ## Executing the examples 17 | 18 | 19 | 20 | ## generateSW 21 | 22 | 23 | 24 | ## injectManifest 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/svelte.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Svelte | Examples 3 | --- 4 | 5 | # Svelte 6 | 7 | The `Svelte` example project can be found on [examples/svelte-routify](https://github.com/vite-pwa/vite-plugin-pwa/tree/main/examples/svelte-routify) package/directory. 8 | 9 | The router used on this example project is [@roxi/routify](https://routify.dev/). 10 | 11 | To test `new content available`, you should rerun the corresponding script, and then refresh the page. 12 | 13 | If you are running an example with `Periodic SW updates`, you will need to wait 1 minute: 14 | 15 | 16 | ## Executing the examples 17 | 18 | 19 | 20 | ## generateSW 21 | 22 | 23 | 24 | ## injectManifest 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/sveltekit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SvelteKit | Examples 3 | --- 4 | 5 | # SvelteKit 6 | 7 | You can find a set of examples in the [@vite-pwa/sveltekit integration repo](https://github.com/vite-pwa/sveltekit/tree/main/examples). 8 | -------------------------------------------------------------------------------- /examples/vitepress.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: VitePress | Examples 3 | --- 4 | 5 | # VitePress 6 | 7 | You can find a set of examples in the [@vite-pwa/vitepress integration repo](https://github.com/vite-pwa/vitepress/tree/main/examples). 8 | 9 | You can also test `VitePress` integration using the source code of this documentation website, you can find it in the [documentation repo](https://github.com/vite-pwa/vite-pwa-docs). 10 | 11 | The behavior used in this website is [Prompt for update](/guide/prompt-for-update). 12 | 13 | To run this site on your local, execute the following script from your shell (from root folder): 14 | ```shell 15 | pnpm run preview 16 | ``` 17 | -------------------------------------------------------------------------------- /examples/vue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vue | Examples 3 | --- 4 | 5 | # Vue 6 | 7 | The `Vue 3` example project can be found on [examples/vue-router](https://github.com/vite-pwa/vite-plugin-pwa/tree/main/examples/vue-router) package/directory. 8 | 9 | The router used on this example project is [vue-router](https://next.router.vuejs.org/). 10 | 11 | To test `new content available`, you should rerun the corresponding script, and then refresh the page. 12 | 13 | If you are running an example with `Periodic SW updates`, you will need to wait 1 minute: 14 | 15 | 16 | ## Executing the examples 17 | 18 | 19 | 20 | ## generateSW 21 | 22 | 23 | 24 | ## injectManifest 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /frameworks/iles.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: îles | Frameworks 3 | --- 4 | 5 | # îles 6 | 7 | We have included the integration with `îles` on their repo, adding `@islands/pwa` module. 8 | 9 | You can find the documentation here: 10 | - [@islands/pwa](https://iles-docs.netlify.app/guide/plugins#islandspwa) 11 | - [Progressive Web Application (PWA)](https://iles-docs.netlify.app/guide/pwa) 12 | -------------------------------------------------------------------------------- /frameworks/preact.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Preact | Frameworks 3 | --- 4 | 5 | # Preact 6 | 7 | You can use the built-in `Vite` virtual module `virtual:pwa-register/preact` for `Preact` which will return `useState` stateful values (`useState`) for `offlineReady` and `needRefresh`. 8 | 9 | ::: warning 10 | You will need to add `workbox-window` as a `dev` dependency to your `Vite` project. 11 | ::: 12 | 13 | ## Type declarations 14 | 15 | ::: tip 16 | 17 | From version `0.14.5` you can also use types definition for preact instead of `vite-plugin-pwa/client`: 18 | ```json 19 | { 20 | "compilerOptions": { 21 | "types": [ 22 | "vite-plugin-pwa/preact" 23 | ] 24 | } 25 | } 26 | ``` 27 | 28 | Or you can add the following reference in any of your `d.ts` files (for example, in `vite-env.d.ts` or `global.d.ts`): 29 | ```ts 30 | /// 31 | ``` 32 | ::: 33 | 34 | ```ts 35 | declare module 'virtual:pwa-register/preact' { 36 | import type { StateUpdater } from 'preact/hooks' 37 | import type { RegisterSWOptions } from 'vite-plugin-pwa/types' 38 | 39 | export type { RegisterSWOptions } 40 | 41 | export function useRegisterSW(options?: RegisterSWOptions): { 42 | needRefresh: [boolean, StateUpdater] 43 | offlineReady: [boolean, StateUpdater] 44 | updateServiceWorker: (reloadPage?: boolean) => Promise 45 | } 46 | } 47 | ``` 48 | 49 | ## Prompt for update 50 | 51 | 52 | 53 | You can use this `ReloadPrompt.tsx` component: 54 | 55 | ::: details ReloadPrompt.tsx 56 | ```tsx 57 | import './ReloadPrompt.css' 58 | 59 | import { useRegisterSW } from 'virtual:pwa-register/preact' 60 | 61 | function ReloadPrompt() { 62 | const { 63 | offlineReady: [offlineReady, setOfflineReady], 64 | needRefresh: [needRefresh, setNeedRefresh], 65 | updateServiceWorker, 66 | } = useRegisterSW({ 67 | onRegistered(r) { 68 | // eslint-disable-next-line prefer-template 69 | console.log('SW Registered: ' + r) 70 | }, 71 | onRegisterError(error) { 72 | console.log('SW registration error', error) 73 | }, 74 | }) 75 | 76 | const close = () => { 77 | setOfflineReady(false) 78 | setNeedRefresh(false) 79 | } 80 | 81 | return ( 82 |
83 | { (offlineReady || needRefresh) 84 | &&
85 |
86 | { offlineReady 87 | ? App ready to work offline 88 | : New content available, click on reload button to update. 89 | } 90 |
91 | { needRefresh && } 92 | 93 |
94 | } 95 |
96 | ) 97 | } 98 | 99 | export default ReloadPrompt 100 | ``` 101 | ::: 102 | 103 | and its corresponding `ReloadPrompt.css` styles file: 104 | 105 | ::: details ReloadPrompt.css 106 | ```css 107 | .ReloadPrompt-container { 108 | padding: 0; 109 | margin: 0; 110 | width: 0; 111 | height: 0; 112 | } 113 | .ReloadPrompt-toast { 114 | position: fixed; 115 | right: 0; 116 | bottom: 0; 117 | margin: 16px; 118 | padding: 12px; 119 | border: 1px solid #8885; 120 | border-radius: 4px; 121 | z-index: 1; 122 | text-align: left; 123 | box-shadow: 3px 4px 5px 0 #8885; 124 | background-color: white; 125 | } 126 | .ReloadPrompt-toast-message { 127 | margin-bottom: 8px; 128 | } 129 | .ReloadPrompt-toast-button { 130 | border: 1px solid #8885; 131 | outline: none; 132 | margin-right: 5px; 133 | border-radius: 2px; 134 | padding: 3px 10px; 135 | } 136 | ``` 137 | ::: 138 | 139 | ## Periodic SW Updates 140 | 141 | As explained in [Periodic Service Worker Updates](/guide/periodic-sw-updates), you can use this code to configure this behavior on your application with the virtual module `virtual:pwa-register/preact`: 142 | 143 | ```ts 144 | import { useRegisterSW } from 'virtual:pwa-register/preact' 145 | 146 | const intervalMS = 60 * 60 * 1000 147 | 148 | const updateServiceWorker = useRegisterSW({ 149 | onRegistered(r) { 150 | r && setInterval(() => { 151 | r.update() 152 | }, intervalMS) 153 | } 154 | }) 155 | ``` 156 | 157 | The interval must be in milliseconds, in the example above it is configured to check the service worker every hour. 158 | 159 | 160 | -------------------------------------------------------------------------------- /frameworks/qwik.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Qwik | Examples 3 | --- 4 | 5 | # Qwik 6 | 7 | Check the [@qwikdev/pwa](https://github.com/QwikDev/pwa) repository for more details, it is still in its early stages. 8 | 9 | This repository is not using `vite-plugin-pwa` directly (maybe in a future), but it is using Workbox. 10 | 11 | 12 | -------------------------------------------------------------------------------- /frameworks/react.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: React | Frameworks 3 | --- 4 | 5 | # React 6 | 7 | You can use the built-in `Vite` virtual module `virtual:pwa-register/react` for `React` which will return `useState` stateful values (`useState`) for `offlineReady` and `needRefresh`. 8 | 9 | ::: warning 10 | You will need to add `workbox-window` as a `dev` dependency to your `Vite` project. 11 | ::: 12 | 13 | ## Type declarations 14 | 15 | ::: tip 16 | 17 | From version `0.14.5` you can also use types definition for react instead of `vite-plugin-pwa/client`, you can use: 18 | ```json 19 | { 20 | "compilerOptions": { 21 | "types": [ 22 | "vite-plugin-pwa/react" 23 | ] 24 | } 25 | } 26 | ``` 27 | 28 | Or you can add the following reference in any of your `d.ts` files (for example, in `vite-env.d.ts` or `global.d.ts`): 29 | ```ts 30 | /// 31 | ``` 32 | ::: 33 | 34 | ```ts 35 | declare module 'virtual:pwa-register/react' { 36 | import type { Dispatch, SetStateAction } from 'react' 37 | import type { RegisterSWOptions } from 'vite-plugin-pwa/types' 38 | 39 | export type { RegisterSWOptions } 40 | 41 | export function useRegisterSW(options?: RegisterSWOptions): { 42 | needRefresh: [boolean, Dispatch>] 43 | offlineReady: [boolean, Dispatch>] 44 | updateServiceWorker: (reloadPage?: boolean) => Promise 45 | } 46 | } 47 | ``` 48 | 49 | ## Prompt for update 50 | 51 | 52 | 53 | You can use this `ReloadPrompt.tsx` component: 54 | 55 | :::details ReloadPrompt.tsx 56 | ```tsx 57 | import React from 'react' 58 | import './ReloadPrompt.css' 59 | 60 | import { useRegisterSW } from 'virtual:pwa-register/react' 61 | 62 | function ReloadPrompt() { 63 | const { 64 | offlineReady: [offlineReady, setOfflineReady], 65 | needRefresh: [needRefresh, setNeedRefresh], 66 | updateServiceWorker, 67 | } = useRegisterSW({ 68 | onRegistered(r) { 69 | // eslint-disable-next-line prefer-template 70 | console.log('SW Registered: ' + r) 71 | }, 72 | onRegisterError(error) { 73 | console.log('SW registration error', error) 74 | }, 75 | }) 76 | 77 | const close = () => { 78 | setOfflineReady(false) 79 | setNeedRefresh(false) 80 | } 81 | 82 | return ( 83 |
84 | { (offlineReady || needRefresh) 85 | &&
86 |
87 | { offlineReady 88 | ? App ready to work offline 89 | : New content available, click on reload button to update. 90 | } 91 |
92 | { needRefresh && } 93 | 94 |
95 | } 96 |
97 | ) 98 | } 99 | 100 | export default ReloadPrompt 101 | ``` 102 | ::: 103 | 104 | and its corresponding `ReloadPrompt.css` styles file: 105 | 106 | :::details ReloadPrompt.css 107 | ```css 108 | .ReloadPrompt-container { 109 | padding: 0; 110 | margin: 0; 111 | width: 0; 112 | height: 0; 113 | } 114 | .ReloadPrompt-toast { 115 | position: fixed; 116 | right: 0; 117 | bottom: 0; 118 | margin: 16px; 119 | padding: 12px; 120 | border: 1px solid #8885; 121 | border-radius: 4px; 122 | z-index: 1; 123 | text-align: left; 124 | box-shadow: 3px 4px 5px 0 #8885; 125 | background-color: white; 126 | } 127 | .ReloadPrompt-toast-message { 128 | margin-bottom: 8px; 129 | } 130 | .ReloadPrompt-toast-button { 131 | border: 1px solid #8885; 132 | outline: none; 133 | margin-right: 5px; 134 | border-radius: 2px; 135 | padding: 3px 10px; 136 | } 137 | ``` 138 | ::: 139 | 140 | ## Periodic SW Updates 141 | 142 | As explained in [Periodic Service Worker Updates](/guide/periodic-sw-updates), you can use this code to configure this behavior on your application with the virtual module `virtual:pwa-register/react`: 143 | 144 | ```ts 145 | import { useRegisterSW } from 'virtual:pwa-register/react' 146 | 147 | const intervalMS = 60 * 60 * 1000 148 | 149 | const updateServiceWorker = useRegisterSW({ 150 | onRegistered(r) { 151 | r && setInterval(() => { 152 | r.update() 153 | }, intervalMS) 154 | } 155 | }) 156 | ``` 157 | 158 | The interval must be in milliseconds, in the example above it is configured to check the service worker every hour. 159 | 160 | 161 | -------------------------------------------------------------------------------- /frameworks/remix.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Remix | Frameworks 3 | next: Getting Started | Examples 4 | outline: deep 5 | --- 6 | 7 | # Remix 8 | 9 | 10 | 11 | ::: warning 12 | This PWA module can only be used with Vite. 13 | ::: 14 | 15 | ## Remix PWA module 16 | 17 | `vite-plugin-pwa` provides the new `@vite-pwa/remix` module that will allow you to use `vite-plugin-pwa` in your Remix applications via `Vite` plugin and `Remix` preset. 18 | 19 | You will need to install `@vite-pwa/remix`: 20 | ::: code-group 21 | ```bash [pnpm] 22 | pnpm add -D @vite-pwa/remix 23 | ``` 24 | ```bash [yarn] 25 | yarn add -D @vite-pwa/remix 26 | ``` 27 | ```bash [npm] 28 | npm install -D @vite-pwa/remix 29 | ``` 30 | ::: 31 | 32 | Then in your Vite configuration file, import the `@vite-pwa/remix` helper and create the Remix PWA Preset and the Vite PWA Plugin and configure them: 33 | ```ts 34 | // vite.config.js 35 | import { vitePlugin as remix } from '@remix-run/dev' 36 | import { installGlobals } from '@remix-run/node' 37 | import { defineConfig } from 'vite' 38 | import { RemixVitePWA } from '@vite-pwa/remix' 39 | 40 | installGlobals() 41 | 42 | const { RemixVitePWAPlugin, RemixPWAPreset } = RemixVitePWA() 43 | 44 | export default defineConfig({ 45 | plugins: [ 46 | remix({ 47 | presets: [RemixPWAPreset()], 48 | }), 49 | RemixVitePWAPlugin({ 50 | // PWA options 51 | }) 52 | ] 53 | }) 54 | ``` 55 | 56 | Check Remix [PWA Options](https://github.com/vite-pwa/remix/blob/main/src/types.ts) for further details. 57 | 58 | ## Custom Service Worker 59 | 60 | When using `injectManifest` strategy, `@vite-pwa/remix` exposes a virtual module `virtual:vite-pwa/remix/sw` with the Remix information you can consume in your service worker (configuration from Remix and the `remix` PWA option): 61 | ```ts 62 | import { 63 | cleanupOutdatedCaches, 64 | clientsClaimMode, 65 | enablePrecaching, 66 | navigateFallback, 67 | promptForUpdate, 68 | staticRoutes, 69 | dynamicRoutes, 70 | routes, 71 | ssr, 72 | } from 'virtual:vite-pwa/remix/sw' 73 | ``` 74 | 75 | If you are using TypeScript you can include `@vite-pwa/remix/remix-sw` in your `tsconfig.json`: 76 | ```json 77 | { 78 | "compilerOptions": { 79 | "types": ["@vite-pwa/remix/remix-sw"] 80 | } 81 | } 82 | ``` 83 | 84 | or just include a triple slash comment in your service worker file: 85 | ```ts 86 | /// 87 | ``` 88 | 89 | You can also import PWA options via `@vite-pwa/remix/sw` (see next section): 90 | ```ts 91 | import { 92 | cleanupOutdatedCaches, 93 | clientsClaimMode, 94 | enablePrecaching, 95 | navigateFallback, 96 | promptForUpdate, 97 | staticRoutes, 98 | dynamicRoutes, 99 | routes, 100 | ssr, 101 | } from '@vite-pwa/remix/sw' 102 | ``` 103 | 104 | ### `setupPwa` helper functions 105 | 106 | `@vite-pwa/remix` provides an internal `setupPWA` module you can use to register a default implementation (similar to Workbox recipes), using the `remix` `PWA options and Remix configuration: 107 | - cleanup outdated caches: Workbox's `cleanupOutdatedCaches` in `generateSW` Workbox build module for `injectManifest` strategy 108 | - clients claim mode: similar to Workbox's `cleanupOutdatedCaches` in `generateSW` Workbox build module for `injectManifest` strategy 109 | - precaching and offline configuration 110 | 111 | You only need to import `setupPWA` from `@vite-pwa/remix/sw` and call it in your service worker: 112 | ```ts 113 | import { setupPwa } from '@vite-pwa/remix/sw' 114 | 115 | setupPwa({ manifest: self.__WB_MANIFEST }) 116 | ``` 117 | 118 | ### Enabling Offline Support 119 | 120 | If your Remix application is an SPA, all routes will be pre-rendered, and you don't need to add additional logic, all html pages will be in the `self.__WB_MANIFEST` array. 121 | 122 | If you're using Remix SSR application, then you need to add [registerRoute](https://developer.chrome.com/docs/workbox/modules/workbox-routing) to handle the SSR routes to avoid default offline browser page when navigate to them: you can import `dynamicRoutes` and `staticRoutes` from the `virtual:vite-pwa/remix/sw` or `@vite-pwa/remix/sw` to register the SSR routes. 123 | 124 | Check the [shared-sw.ts module](https://github.com/vite-pwa/remix/blob/main/examples/pwa-simple-sw/app/shared-sw.ts) and the usage in the [service worker](https://github.com/vite-pwa/remix/blob/main/examples/pwa-simple-sw/app/plain-sw.ts), remember to exclude the router in dev server. 125 | 126 | ## PWA Assets 127 | 128 | This feature includes the following components: 129 | - `PwaManifest` component to include the PWA manifest in your HTML pages: will inject the PWA web manifest in the HTML head 130 | - `PwaAssets` component to include the PWA assets in your HTML pages: will inject the PWA assets in the HTML head (PWA web manifest, theme-color, favicon and PWA web manifest) 131 | 132 | ## Remix PWA Alternative 133 | 134 | You can use [Remix PWA](https://remix-pwa.run/) to add PWA support to your Remix application. 135 | 136 | 137 | -------------------------------------------------------------------------------- /frameworks/solidjs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SolidJS | Frameworks 3 | --- 4 | 5 | # SolidJS 6 | 7 | You can use the built-in `Vite` virtual module `virtual:pwa-register/solid` for `SolidJS` which will return `createSignal` stateful values (`createSignal`) for `offlineReady` and `needRefresh`. 8 | 9 | ::: warning 10 | You will need to add `workbox-window` as a `dev` dependency to your `Vite` project. 11 | ::: 12 | 13 | ## Type declarations 14 | 15 | ::: tip 16 | 17 | From version `0.14.5` you can also use types definition for solid instead of `vite-plugin-pwa/client`: 18 | ```json 19 | { 20 | "compilerOptions": { 21 | "types": [ 22 | "vite-plugin-pwa/solid" 23 | ] 24 | } 25 | } 26 | ``` 27 | 28 | Or you can add the following reference in any of your `d.ts` files (for example, in `vite-env.d.ts` or `global.d.ts`): 29 | ```ts 30 | /// 31 | ``` 32 | ::: 33 | 34 | ```ts 35 | declare module 'virtual:pwa-register/solid' { 36 | import type { Accessor, Setter } from 'solid-js' 37 | import type { RegisterSWOptions } from 'vite-plugin-pwa/types' 38 | 39 | export type { RegisterSWOptions } 40 | 41 | export function useRegisterSW(options?: RegisterSWOptions): { 42 | needRefresh: [Accessor, Setter] 43 | offlineReady: [Accessor, Setter] 44 | updateServiceWorker: (reloadPage?: boolean) => Promise 45 | } 46 | } 47 | ``` 48 | 49 | ## Prompt for update 50 | 51 | You can use this `ReloadPrompt.tsx` component: 52 | 53 | ::: details ReloadPrompt.tsx 54 | ```tsx 55 | import type { Component } from 'solid-js' 56 | import { Show } from 'solid-js' 57 | import { useRegisterSW } from 'virtual:pwa-register/solid' 58 | import styles from './ReloadPrompt.module.css' 59 | 60 | const ReloadPrompt: Component = () => { 61 | const { 62 | offlineReady: [offlineReady, setOfflineReady], 63 | needRefresh: [needRefresh, setNeedRefresh], 64 | updateServiceWorker, 65 | } = useRegisterSW({ 66 | onRegistered(r) { 67 | // eslint-disable-next-line prefer-template 68 | console.log('SW Registered: ' + r) 69 | }, 70 | onRegisterError(error) { 71 | console.log('SW registration error', error) 72 | }, 73 | }) 74 | 75 | const close = () => { 76 | setOfflineReady(false) 77 | setNeedRefresh(false) 78 | } 79 | 80 | return ( 81 |
82 | 83 |
84 |
85 | New content available, click on reload button to update.} 87 | when={offlineReady()} 88 | > 89 | App ready to work offline 90 | 91 |
92 | 93 | 94 | 95 | 96 |
97 |
98 |
99 | ) 100 | } 101 | 102 | export default ReloadPrompt 103 | ``` 104 | ::: 105 | 106 | and its corresponding `ReloadPrompt.module.css` styles module: 107 | 108 | ::: details ReloadPrompt.module.css 109 | ```css 110 | .Container { 111 | padding: 0; 112 | margin: 0; 113 | width: 0; 114 | height: 0; 115 | } 116 | .Toast { 117 | position: fixed; 118 | right: 0; 119 | bottom: 0; 120 | margin: 16px; 121 | padding: 12px; 122 | border: 1px solid #8885; 123 | border-radius: 4px; 124 | z-index: 1; 125 | text-align: left; 126 | box-shadow: 3px 4px 5px 0 #8885; 127 | background-color: white; 128 | } 129 | .ToastMessage { 130 | margin-bottom: 8px; 131 | } 132 | .ToastButton { 133 | border: 1px solid #8885; 134 | outline: none; 135 | margin-right: 5px; 136 | border-radius: 2px; 137 | padding: 3px 10px; 138 | } 139 | ``` 140 | ::: 141 | 142 | ## Periodic SW Updates 143 | 144 | As explained in [Periodic Service Worker Updates](/guide/periodic-sw-updates), you can use this code to configure this behavior on your application with the virtual module `virtual:pwa-register/solid`: 145 | 146 | ```ts 147 | import { useRegisterSW } from 'virtual:pwa-register/solid' 148 | 149 | const intervalMS = 60 * 60 * 1000 150 | 151 | const updateServiceWorker = useRegisterSW({ 152 | onRegistered(r) { 153 | r && setInterval(() => { 154 | r.update() 155 | }, intervalMS) 156 | } 157 | }) 158 | ``` 159 | 160 | The interval must be in milliseconds, in the example above it is configured to check the service worker every hour. 161 | 162 | 163 | -------------------------------------------------------------------------------- /frameworks/svelte.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Svelte | Frameworks 3 | --- 4 | 5 | # Svelte 6 | 7 | You can use the built-in `Vite` virtual module `virtual:pwa-register/svelte` for `Svelte` which will return `writable` stores (`Writable`) for `offlineReady` and `needRefresh`. 8 | 9 | ::: warning 10 | You will need to add `workbox-window` as a `dev` dependency to your `Vite` project. 11 | ::: 12 | 13 | ## Type declarations 14 | 15 | ::: tip 16 | 17 | From version `0.14.5` you can also use types definition for svelte instead of `vite-plugin-pwa/client`: 18 | ```json 19 | { 20 | "compilerOptions": { 21 | "types": [ 22 | "vite-plugin-pwa/svelte" 23 | ] 24 | } 25 | } 26 | ``` 27 | 28 | Or you can add the following reference in any of your `d.ts` files (for example, in `vite-env.d.ts` or `global.d.ts`): 29 | ```ts 30 | /// 31 | ``` 32 | ::: 33 | 34 | ```ts 35 | declare module 'virtual:pwa-register/svelte' { 36 | import type { Writable } from 'svelte/store' 37 | import type { RegisterSWOptions } from 'vite-plugin-pwa/types' 38 | 39 | export type { RegisterSWOptions } 40 | 41 | export function useRegisterSW(options?: RegisterSWOptions): { 42 | needRefresh: Writable 43 | offlineReady: Writable 44 | updateServiceWorker: (reloadPage?: boolean) => Promise 45 | } 46 | } 47 | ``` 48 | 49 | ## Prompt for update 50 | 51 | You can use this `ReloadPrompt.svelte` component: 52 | 53 | ::: details ReloadPrompt.svelte 54 | ```html 55 | 74 | 75 | {#if toast} 76 | 100 | {/if} 101 | 102 | 127 | ``` 128 | ::: 129 | 130 | ## Periodic SW Updates 131 | 132 | As explained in [Periodic Service Worker Updates](/guide/periodic-sw-updates), you can use this code to configure this behavior on your application with the virtual module `virtual:pwa-register/svelte`: 133 | 134 | ```ts 135 | import { useRegisterSW } from 'virtual:pwa-register/svelte' 136 | 137 | const intervalMS = 60 * 60 * 1000 138 | 139 | const updateServiceWorker = useRegisterSW({ 140 | onRegistered(r) { 141 | r && setInterval(() => { 142 | r.update() 143 | }, intervalMS) 144 | } 145 | }) 146 | ``` 147 | 148 | The interval must be in milliseconds, in the example above it is configured to check the service worker every hour. 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /guide/auto-update.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Automatic reload | Guide 3 | outline: deep 4 | --- 5 | 6 | # Automatic reload 7 | 8 | With this behavior, once the browser detects a new version of your application, then, it will update the caches and will reload any browser windows/tabs with the application opened automatically to take the control. 9 | 10 | ::: warning 11 | In order to reload all client tab/window, you will need to import any virtual module provided by the plugin: if you're not using any virtual, there is no way to interact with the application ui, and so, any client tab/window will not be reloaded (the old service worker will be still controlling the application). 12 | 13 | Automatic reload is not automatic page reload, you will need to use the following code in your application entry point if you want **automatic page reload**: 14 | 15 | ```js 16 | import { registerSW } from 'virtual:pwa-register' 17 | 18 | registerSW({ immediate: true }) 19 | ``` 20 | ::: 21 | 22 | The disadvantage of using this behavior is that the user can lose data in any browser windows/tabs in which the application is open and is filling in a form. 23 | 24 | If your application has forms, we recommend you to change the behavior to use default `prompt` option to allow the user decide when to update the content of the application. 25 | 26 | ::: danger 27 | Before you put your application into production, you need to be sure of the behavior you want for the service worker. Changing the behavior of the service worker from `autoUpdate` to `prompt` can be a pain. 28 | ::: 29 | 30 | ## Plugin Configuration 31 | 32 | With this option, the plugin will force `workbox.clientsClaim` and `workbox.skipWaiting` to `true` on the plugin options. 33 | 34 | You must add `registerType: 'autoUpdate'` to `vite-plugin-pwa` plugin options in your `vite.config.ts` file: 35 | 36 | ```ts 37 | VitePWA({ 38 | registerType: 'autoUpdate' 39 | }) 40 | ``` 41 | 42 | ### Cleanup Outdated Caches 43 | 44 | 45 | 46 | 47 | 48 | ### Inject Manifest Source Map 49 | 50 | 51 | 52 | ### Generate SW Source Map 53 | 54 | 55 | 56 | ## Importing Virtual Modules 57 | 58 | With this behavior, you **must** import one of the virtual modules exposed by `vite-plugin-pwa` plugin **only** if you need to prompt a dialog to the user when the application is ready to work offline, otherwise you can import or just omit it. 59 | 60 | If you don't import one of the virtual modules, the automatic reload will still work. 61 | 62 | ### Ready To Work Offline 63 | 64 | You must include the following code on your `main.ts` or `main.js` file: 65 | 66 | ```ts 67 | import { registerSW } from 'virtual:pwa-register' 68 | 69 | const updateSW = registerSW({ 70 | onOfflineReady() {}, 71 | }) 72 | ``` 73 | 74 | You will need to show a ready to work offline dialog to the user with an `OK` button inside `onOfflineReady` callback. 75 | 76 | When the user clicks the `OK` button, just hide the prompt shown on `onOfflineReady` method. 77 | 78 | ### SSR/SSG 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /guide/cookbook.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vite, Rollup, PWA and Workbox Cookbook | Guide 3 | outline: deep 4 | --- 5 | 6 | 15 | 16 | # Vite, Rollup, PWA and Workbox cookbook 17 | 18 | In this page we're going to explain how `vite-plugin-pwa` builds the service worker. 19 | 20 | You can open Excalidraw source diagram for the SVG images. 21 | 22 | ## Vite config file 23 | 24 |
25 | 26 | ## Vite Build CLI 27 | 28 |
29 | 30 | ## vite-plugin-pwa closeBundle hook 31 | 32 |
33 | 34 | ## workbox-build injectManifest 35 | 36 |
37 | -------------------------------------------------------------------------------- /guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started | Guide 3 | --- 4 | 5 | # Getting Started 6 | 7 | Progressive Web Apps (PWAs) are web applications built and enhanced with modern APIs to deliver enhanced capabilities, reliability, and installability while reaching anyone, anywhere, on any device—all with a single codebase. 8 | 9 | At a high level, a PWA consists of a [web application manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) to give the browser information about your app, and a service worker to manage the offline experience. 10 | 11 | If you are new to Progressive Web Apps, you might consider reading Google's ["Learn PWA"](https://web.dev/learn/pwa/) course before you begin. 12 | 13 | ## Service Worker 14 | 15 | Service workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available). They are intended, among other things, to enable the creation of effective offline experiences, intercept network requests and take appropriate action based on whether the network is available, and update assets residing on the server. They will also allow access to push notifications and background sync APIs. 16 | 17 | A service worker is an event-driven [worker](https://developer.mozilla.org/en-US/docs/Web/API/Worker) registered against an origin and a path. It takes the form of a JavaScript file that can control the web-page/site that it is associated with, intercepting and modifying navigation and resource requests, and caching resources in a very granular fashion to give you complete control over how your app behaves in certain situations (the most obvious one being when the network is not available). 18 | 19 | You can find more information about service workers in [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API). 20 | 21 | ## Vite PWA 22 | 23 | Vite PWA will help you to turn your existing applications into PWAs with very little configuration. It comes preset with sensible defaults for common use cases. 24 | 25 | The `vite-plugin-pwa` plugin can: 26 | 27 | - Generate the [web appplication manifest][webmanifest] and add it to your entry point (see the [setup guide for manifest generation](pwa-minimal-requirements#web-app-manifest)). 28 | - Generate the service worker using the `strategies` option (for more information, see ["Service Worker Strategies"](/guide/service-worker-strategies-and-behaviors#service-worker-strategies) section) 29 | - Generate a script to register the service worker in the browser (see the ["Register Service Worker"](/guide/register-service-worker) section) 30 | 31 | ## Scaffolding Your First Vite PWA Project 32 | 33 | 34 | 35 | ## Installing vite-plugin-pwa 36 | 37 | To install the `vite-plugin-pwa` plugin, just add it to your project as a `dev dependency`: 38 | 39 | ::: code-group 40 | ```bash [pnpm] 41 | pnpm add -D vite-plugin-pwa 42 | ``` 43 | ```bash [yarn] 44 | yarn add -D vite-plugin-pwa 45 | ``` 46 | ```bash [npm] 47 | npm install -D vite-plugin-pwa 48 | ``` 49 | ::: 50 | 51 | ## Configuring vite-plugin-pwa 52 | 53 | Edit your `vite.config.js / vite.config.ts` file and add the `vite-plugin-pwa`: 54 | 55 | ```ts 56 | import { VitePWA } from 'vite-plugin-pwa' 57 | 58 | export default defineConfig({ 59 | plugins: [ 60 | VitePWA({ registerType: 'autoUpdate' }) 61 | ] 62 | }) 63 | ``` 64 | 65 | With this minimal configuration of the `vite-plugin-pwa` plugin, your application is now able to generate the [Web App Manifest][webmanifest] and inject it at the entry point, generate the service worker and register it in the browser. 66 | 67 | You can find the full list of the `vite-plugin-pwa` plugin configuration options in the following [client.d.ts](https://github.com/antfu/vite-plugin-pwa/blob/main/src/types.ts). 68 | 69 | ::: warning 70 | If you are **NOT** using `vite-plugin-pwa` version `0.12.2+`, there is a bug handling `injectRegister` (the service worker generated will not include the code required to allow work with `autoUpdate` behavior). 71 | 72 | If you're using a `vite-plugin-pwa` plugin version prior to `0.12.2`, you can fix the bug using this plugin configuration: 73 | ```ts 74 | import { VitePWA } from 'vite-plugin-pwa' 75 | 76 | export default defineConfig({ 77 | plugins: [ 78 | VitePWA({ 79 | registerType: 'autoUpdate', 80 | workbox: { 81 | clientsClaim: true, 82 | skipWaiting: true 83 | } 84 | }) 85 | ] 86 | }) 87 | ``` 88 | ::: 89 | 90 | If you want to check it in `dev`, add the `devOptions` option to the plugin configuration (you will have the [Web App Manifest][webmanifest] and the generated service worker): 91 | ```ts 92 | import { VitePWA } from 'vite-plugin-pwa' 93 | 94 | export default defineConfig({ 95 | plugins: [ 96 | VitePWA({ 97 | registerType: 'autoUpdate', 98 | devOptions: { 99 | enabled: true 100 | } 101 | }) 102 | ] 103 | }) 104 | ``` 105 | 106 | If you build your application, the [Web App Manifest][webmanifest] will be generated and configured on the application entry point, the service worker will be also generated and the script/module to register it in the browser added. 107 | 108 | ::: info 109 | `vite-plugin-pwa` plugin uses [workbox-build](https://developer.chrome.com/docs/workbox/modules/workbox-build) node library to build the service worker, you can find more information in the [Service Worker Strategies And Behaviors](/guide/service-worker-strategies-and-behaviors) and [Workbox](/workbox/) sections. 110 | ::: 111 | 112 | [webmanifest]: https://developer.mozilla.org/en-US/docs/Web/Manifest 113 | -------------------------------------------------------------------------------- /guide/inject-manifest.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced (injectManifest) | Guide 3 | outline: deep 4 | --- 5 | 6 | # Advanced (injectManifest) 7 | 8 | With this service worker `strategy` you can build your own service worker. 9 | 10 | The `vite-plugin-pwa` plugin will compile your custom service worker and inject its service worker's precache manifest. 11 | 12 | By default, the plugin will assume the `service worker` source code is located at the `Vite's public` folder with the name `sw.js`, that's, it will search in the following file: `/public/sw.js`. 13 | 14 | If you want to change the location and/or the service worker name, you will need to change the following plugin options: 15 | - `srcDir`: **must** be relative to the project root folder 16 | - `filename`: including the file extension and **must** be relative to the `srcDir` folder 17 | 18 | For example, if your service worker is located at `/src/my-sw.js` you must configure it using: 19 | ```ts 20 | import { VitePWA } from 'vite-plugin-pwa' 21 | 22 | export default defineConfig({ 23 | plugins: [ 24 | VitePWA({ 25 | strategies: 'injectManifest', 26 | srcDir: 'src', 27 | filename: 'my-sw.js' 28 | }) 29 | ] 30 | }) 31 | ``` 32 | 33 | ## Custom Service worker 34 | 35 | We recommend you to use [Workbox](https://developer.chrome.com/docs/workbox/) to build your service worker instead using `importScripts`, you will need to include `workbox-*` dependencies as `dev dependencies` to your project. 36 | 37 | ### Plugin Configuration 38 | 39 | You **must** configure `strategies: 'injectManifest'` in `vite-plugin-pwa` plugin options in your `vite.config.ts` file: 40 | 41 | ```ts 42 | VitePWA({ 43 | strategies: 'injectManifest', 44 | }) 45 | ``` 46 | 47 | ### Development 48 | 49 | If you would like the service worker to run in development, make sure to enable it in the [devOptions](/guide/development#plugin-configuration) and to set the type to [module](/guide/development#injectmanifest-strategy) if required. 50 | 51 | ### Service Worker Code 52 | 53 | Your custom service worker (`public/sw.js`) should have at least this code (you also need to install `workbox-precaching` as `dev dependency` to your project): 54 | ```js 55 | import { precacheAndRoute } from 'workbox-precaching' 56 | 57 | precacheAndRoute(self.__WB_MANIFEST) 58 | ``` 59 | 60 | If you're not using `precaching` (`self.__WB_MANIFEST`), you need to disable `injection point` to avoid compilation errors (available only from version `^0.14.0`), add the following option to your pwa configuration: 61 | 62 | ```ts 63 | injectManifest: { 64 | injectionPoint: undefined 65 | } 66 | ``` 67 | 68 | ### Service worker errors on browser 69 | 70 | 71 | 72 | ### Cleanup Outdated Caches 73 | 74 | 75 | 76 | 77 | 78 | ### Inject Manifest Source Map 79 | 80 | 81 | 82 | ### Custom Rollup and Vite Plugins 83 | 84 | From `v0.18.0`, you can add custom Rollup and/or Vite plugins to the service worker build, using `rollup` and `vite` options in the new `buildPlugins` option. 85 | 86 | ::: warning 87 | The old `plugins` option has been deprecated, use `buildPlugins.rollup` instead: 88 | - if `buildPlugins.rollup` is configured then `plugins` will be ignored 89 | - if `buildPlugins.rollup` is not configured then `plugins` will be used 90 | ::: 91 | 92 | You can check the [vue-router example](https://github.com/vite-pwa/vite-plugin-pwa/tree/main/examples/vue-router) using a custom Vite plugin with a simple virtual module consumed by both custom service workers. 93 | 94 | ## Auto Update Behavior 95 | 96 | If you need your custom service worker works with `Auto Update` behavior, you need to change the plugin configuration options and add some custom code to your service worker code. 97 | 98 | ### Plugin Configuration 99 | 100 | You must configure `registerType: 'autoUpdate'` to `vite-plugin-pwa` plugin options in your `vite.config.ts` file: 101 | 102 | ```ts 103 | VitePWA({ 104 | registerType: 'autoUpdate' 105 | }) 106 | ``` 107 | 108 | ### Service Worker Code 109 | 110 | You **must** include in your service worker code at least this code (you also need to install `workbox-core` as `dev dependency` to your project): 111 | 112 | ```js 113 | import { clientsClaim } from 'workbox-core' 114 | 115 | self.skipWaiting() 116 | clientsClaim() 117 | ``` 118 | 119 | ## Prompt For Update Behavior 120 | 121 | If you need your custom service worker works with `Prompt For Update` behavior, you need to change your service worker code. 122 | 123 | ### Service Worker Code 124 | 125 | You need to include on your service worker at least this code: 126 | 127 | ```js 128 | self.addEventListener('message', (event) => { 129 | if (event.data && event.data.type === 'SKIP_WAITING') 130 | self.skipWaiting() 131 | }) 132 | ``` 133 | 134 | ## TypeScript support 135 | 136 | You can use TypeScript to write your custom service worker. To resolve service worker types, just add `WebWorker` to `lib` entry on your `tsconfig.json` file: 137 | 138 | ```json 139 | { 140 | "compilerOptions": { 141 | "lib": ["ESNext", "DOM", "WebWorker"] 142 | } 143 | } 144 | ``` 145 | 146 | ### Plugin Configuration 147 | 148 | We recommend you to put your custom service worker inside `src` directory. 149 | 150 | You need to configure `srcDir: 'src'` and `filename: 'sw.ts'` plugin options in your `vite.config.ts` file, configure both options with the directory and the name of your custom service worker properly: 151 | 152 | ```ts 153 | VitePWA({ 154 | srcDir: 'src', 155 | filename: 'sw.ts' 156 | }) 157 | ``` 158 | 159 | ### Service Worker Code 160 | 161 | You need to define `self` scope with `ServiceWorkerGlobalScope`: 162 | 163 | ```ts 164 | import { precacheAndRoute } from 'workbox-precaching' 165 | 166 | declare let self: ServiceWorkerGlobalScope 167 | 168 | precacheAndRoute(self.__WB_MANIFEST) 169 | ``` 170 | -------------------------------------------------------------------------------- /guide/periodic-sw-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Periodic Service Worker Updates | Guide 3 | --- 4 | 5 | # Periodic Service Worker Updates 6 | 7 | :::info 8 | If you're not importing any of the virtual modules provided by `vite-plugin-pwa` you'll need to figure out how to configure it, it is out of the scope of this guide. 9 | ::: 10 | 11 | As explained in [Manual Updates](https://web.dev/articles/service-worker-lifecycle#manual_updates) entry in [The Service Worker Lifecycle](https://web.dev/articles/service-worker-lifecycle) article, you can use this code to configure periodic service worker updates on your application on your `main.ts` or `main.js`: 12 | 13 | ::: details main.ts / main.js 14 | ```ts 15 | import { registerSW } from 'virtual:pwa-register' 16 | 17 | const intervalMS = 60 * 60 * 1000 18 | 19 | const updateSW = registerSW({ 20 | onRegistered(r) { 21 | r && setInterval(() => { 22 | r.update() 23 | }, intervalMS) 24 | } 25 | }) 26 | ``` 27 | ::: 28 | 29 | The interval must be in milliseconds, in the example above it is configured to check the service worker every hour. 30 | 31 | ## Handling Edge Cases 32 | 33 | ::: info 34 | From version `0.12.8+` we have a new option, `onRegisteredSW`, and `onRegistered` has been deprecated. If `onRegisteredSW` is present, `onRegistered` will never be called. 35 | ::: 36 | 37 | Previous script will allow you to check if there is a new version of your application available, but you will need also to deal with some edge cases like: 38 | - server is down when calling the update method 39 | - the user can go offline at any time 40 | 41 | To mitigate previous problems, use this more complex snippet: 42 | 43 | ::: details main.ts / main.js 44 | ```ts 45 | import { registerSW } from 'virtual:pwa-register' 46 | 47 | const intervalMS = 60 * 60 * 1000 48 | 49 | const updateSW = registerSW({ 50 | onRegisteredSW(swUrl, r) { 51 | r && setInterval(async () => { 52 | if (r.installing || !navigator) 53 | return 54 | 55 | if (('connection' in navigator) && !navigator.onLine) 56 | return 57 | 58 | const resp = await fetch(swUrl, { 59 | cache: 'no-store', 60 | headers: { 61 | 'cache': 'no-store', 62 | 'cache-control': 'no-cache', 63 | }, 64 | }) 65 | 66 | if (resp?.status === 200) 67 | await r.update() 68 | }, intervalMS) 69 | } 70 | }) 71 | ``` 72 | ::: 73 | 74 | 75 | -------------------------------------------------------------------------------- /guide/prompt-for-update.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Prompt for new content refreshing | Guide 3 | outline: deep 4 | --- 5 | 6 | # Prompt for new content refreshing 7 | 8 | 9 | 10 | ## Plugin Configuration 11 | 12 | Since this is the default behavior for the `registerType` plugin option, you don't need to configure it. 13 | 14 | ### Cleanup Outdated Caches 15 | 16 | 17 | 18 | 19 | 20 | ### Inject Manifest Source Map 21 | 22 | 23 | 24 | ### Generate SW Source Map 25 | 26 | 27 | 28 | ## Importing Virtual Modules 29 | 30 | You must include the following code on your `main.ts` or `main.js` file: 31 | ```ts 32 | import { registerSW } from 'virtual:pwa-register' 33 | 34 | const updateSW = registerSW({ 35 | onNeedRefresh() {}, 36 | onOfflineReady() {}, 37 | }) 38 | ``` 39 | 40 | You will need to: 41 | - show a prompt to the user with refresh and cancel buttons inside `onNeedRefresh` method. 42 | - show a ready to work offline message to the user with an OK button inside `onOfflineReady` method. 43 | 44 | When the user clicks the "refresh" button when `onNeedRefresh` called, then call `updateSW()` function; the page will reload and the up-to-date content will be served. 45 | 46 | In any case, when the user clicks the `Cancel` or `OK` buttons in case `onNeedRefresh` or `onOfflineReady` respectively, close the corresponding showed prompt. 47 | 48 | ### SSR/SSG 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /guide/pwa-minimal-requirements.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PWA Minimal Requirements | Guide 3 | --- 4 | 5 | # PWA Minimal Requirements 6 | 7 | Previous steps in this guide, are the minimal requirements and configuration to create the [Web App Manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) and the service worker when you build your application, but you'll need to include more options to meet PWA Minimal Requirements. 8 | 9 | Your application **must** meet the PWA Minimal Requirements before deploying it to production or when testing your build on local: for example, when testing your PWA application on local using `LightHouse`. 10 | 11 | To make your PWA application installable (one of the requirements), you will need to modify your application entry point, add some minimal entries to your `Web App Manifest`, allow search engines to crawl all your application pages and configure your server properly (only for production, on local you can use `https-localhost` dependency and `node`). 12 | 13 | Check also the new [PWA Minimal Requirements](/assets-generator/#pwa-minimal-icons-requirements) page in the [PWA Assets Generator](/assets-generator/) section. 14 | 15 | ## Entry Point 16 | 17 | Your application entry point (usually `index.html`) **must** have the following entries in the `` section: 18 | - mobile viewport configuration 19 | - a title 20 | - a description 21 | - a favicon, check the following pages: https://dev.to/masakudamatsu/favicon-nightmare-how-to-maintain-sanity-3al7 and this old one https://www.leereamsnyder.com/blog/favicons-in-2021 22 | - a link for `apple-touch-icon` 23 | - a link for `mask-icon` (right now there is no need to provide a `mask-icon`) 24 | - a meta entry for `theme-color` 25 | 26 | For example, a minimal configuration (you must provide all the icons and images): 27 | ```html 28 | 29 | 30 | My Awesome App 31 | 32 | 33 | 34 | 35 | 36 | 37 | ``` 38 | 39 | ## Web App Manifest 40 | 41 | Your application [Web App Manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) **must** have the following entries: 42 | - a scope: omitted here for simplicity, the `vite-plugin-pwa` plugin will use the `Vite` base option to configure it (default is `/`) 43 | - a name 44 | - a short description 45 | - a description 46 | - a `theme_color`: **must match** the configured one on `Entry Point theme-color` 47 | - an icon with `192x192` size 48 | - an icon with `512x512` size 49 | 50 | To configure the [Web App Manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest), add the `manifest` entry to the `vite-plugin-pwa` plugin options. 51 | 52 | Following with the example, here a minimal configuration (you must provide all the icons and images): 53 | ```ts 54 | import { VitePWA } from 'vite-plugin-pwa' 55 | 56 | export default defineConfig({ 57 | plugins: [ 58 | VitePWA({ 59 | includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'mask-icon.svg'], 60 | manifest: { 61 | name: 'My Awesome App', 62 | short_name: 'MyApp', 63 | description: 'My Awesome App description', 64 | theme_color: '#ffffff', 65 | icons: [ 66 | { 67 | src: 'pwa-192x192.png', 68 | sizes: '192x192', 69 | type: 'image/png' 70 | }, 71 | { 72 | src: 'pwa-512x512.png', 73 | sizes: '512x512', 74 | type: 'image/png' 75 | } 76 | ] 77 | } 78 | }) 79 | ] 80 | }) 81 | ``` 82 | 83 | You can also specify `manifest: false` to disable the `Web App Manifest` generation adding your own `manifest.webmanifest/manifest.json` file to the `public` folder on your application. 84 | 85 | The `vite-plugin-pwa` has the full definition of the `Web App Manifest` options, if you want to have DX support when using your own web manifest, add the following entry to your custom web manifest (VSCode and JetBrains IDEs will use it to provide DX support): 86 | ```json 87 | { 88 | "$schema": "https://json.schemastore.org/web-manifest-combined.json" 89 | } 90 | ``` 91 | 92 | ## Icons / Images 93 | 94 | :::tip 95 | Check out the [PWA Assets Generator](/assets-generator/) to generate all the icons and images required for your PWA application. 96 | 97 | You can also use [PWA Builder Image Generator](https://www.pwabuilder.com/imageGenerator) to generate all your PWA application's icons. 98 | ::: 99 | 100 | For `manifest` icons entry, you will need to create `pwa-192x192.png`, and `pwa-512x512.png` icons. The icons specified above are the minimum required to meet PWA, that is, icons with `192x192` and `512x512` resolutions. 101 | 102 | We suggest creating a svg or png icon (if it is a png icon, with the maximum resolution possible) for your application and use it to generate your PWA icons: 103 | - [PWA Assets Generator](/assets-generator/) (recommended). 104 | - [Favicon InBrowser.App](https://favicon.inbrowser.app/tools/favicon-generator) (recommended). 105 | - [Favicon Generator](https://realfavicongenerator.net/). 106 | 107 | For `mask-icon` in the entry point, use the svg or the png used to generate the favicon package. 108 | 109 | Once generated, download the ZIP and use `android-*` icons for `pwa-*`: 110 | - use `android-chrome-192x192.png` for `pwa-192x192.png` 111 | - use `android-chrome-512x512.png` for `pwa-512x512.png` 112 | - `apple-touch-icon.png` is `apple-touch-icon.png` 113 | - `favicon.ico` is `favicon.ico` 114 | 115 | If you want you can add the `purpose: 'any maskable'` icon to the Web App Manifest, but it is better to add 2 icons with `any` and `maskable` purposes: 116 | ```ts 117 | icons: [ 118 | { 119 | src: 'pwa-192x192.png', 120 | sizes: '192x192', 121 | type: 'image/png' 122 | }, 123 | { 124 | src: 'pwa-512x512.png', 125 | sizes: '512x512', 126 | type: 'image/png' 127 | }, 128 | { 129 | src: 'pwa-512x512.png', 130 | sizes: '512x512', 131 | type: 'image/png', 132 | purpose: 'any' 133 | }, 134 | { 135 | src: 'pwa-512x512.png', 136 | sizes: '512x512', 137 | type: 'image/png', 138 | purpose: 'maskable' 139 | } 140 | ] 141 | ``` 142 | 143 | ## Search Engines 144 | 145 | You **must** add a `robots.txt` file to allow search engines to crawl all your application pages, just add `robots.txt` to the `public` folder on your application: 146 | ```txt 147 | User-agent: * 148 | Allow: / 149 | ``` 150 | 151 | :::warning 152 | `public` folder must be on the root folder of your application, not inside the `src` folder. 153 | ::: 154 | 155 | ## Server Configuration 156 | 157 | You can use the server you want, but your server **must**: 158 | - serve `manifest.webmanifest` with `application/manifest+json` mime type 159 | - serve your application over `https` 160 | - redirect from `http` to `https` 161 | 162 | You can find more information in the [Deploy](/deployment/) section. 163 | -------------------------------------------------------------------------------- /guide/register-service-worker.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Register Service Worker | Guide 3 | --- 4 | 5 | # Register Service Worker 6 | 7 | `vite-plugin-pwa` plugin will register the service worker automatically for you, using the `injectRegister` configuration option (**optional**). 8 | 9 | If you want to configure the `injectRegister` plugin option: 10 | ```ts 11 | import { VitePWA } from 'vite-plugin-pwa' 12 | 13 | export default defineConfig({ 14 | plugins: [ 15 | VitePWA({ 16 | injectRegister: 'auto' 17 | }) 18 | ] 19 | }) 20 | ``` 21 | 22 | The `injectRegister` plugin configuration option, will control how to register the service worker in your application: 23 | - `inline`: injects a simple register script, inlined in the application entry point 24 | - `script`: injects a `script` tag in the `head` with the `src` attribute to a generated script to register the service worker 25 | - `script-defer` : injects a `script` tag with `defer` attribute in the `head` with the `src` attribute to a generated script to register the service worker 26 | - `null` (manual): do nothing, you will need to register the service worker yourself, or import any of the virtual modules exposed by the plugin 27 | - **`auto` (default value)**: depends on whether you use any of the virtual modules exposed by the plugin, it will do nothing or switch to `script` mode 28 | 29 | You can find more information about the virtual modules exposed by the plugin in the [Frameworks](/frameworks/) section. 30 | 31 | ## Inline Registration 32 | 33 | When configuring `injectRegister: 'inline'` in the plugin configuration, the plugin will inline a head script adding in to your application entry point: 34 | ::: details **inlined head script** 35 | ```html 36 | 37 | 44 | 45 | ``` 46 | ::: 47 | 48 | ## Script Registration 49 | 50 | When configuring `injectRegister: 'script' | 'script-defer'` in the plugin configuration, the plugin will generate a `registerSW.js` script adding it to your application entry point: 51 | ::: details **head script** 52 | ```html 53 | 54 | 55 | 56 | ``` 57 | ::: 58 | 59 | ::: details **/registerSW.js** 60 | ```js 61 | if ('serviceWorker' in navigator) { 62 | window.addEventListener('load', () => { 63 | navigator.serviceWorker.register('/sw.js', { scope: '/' }) 64 | }) 65 | } 66 | ``` 67 | ::: 68 | 69 | ## Manual Registration 70 | 71 | When configuring `injectRegister: null` in the plugin configuration, the plugin will do nothing, you must register the service workbox manually yourself. 72 | 73 | Or you can import any of the virtual modules exposed by the plugin. 74 | 75 | If you're using `injectManifest` strategy in development with `devOptions` enabled, you should check [injectManifest development section](/guide/development#injectmanifest-strategy) to get details on getting the right ServiceWorker URL for your development setup. 76 | 77 | 78 | ## Auto Registration 79 | 80 | If your application code base is not importing any of the virtual modules exposed by the plugin, the plugin will fallback to [Script Registration](/guide/register-service-worker#script-registration), otherwise, the imported virtual module will register the service worker for you. 81 | -------------------------------------------------------------------------------- /guide/scaffolding.md: -------------------------------------------------------------------------------- 1 | # Scaffolding Your First Vite PWA Project 2 | 3 | ::: tip 4 | From version `v0.6.0`, all the templates to use Vite 6, including also the latest frameworks changes. 5 | 6 | Use version `v0.5.0` for Vite 5 and previous versions of the frameworks. 7 | ::: 8 | 9 | 10 | -------------------------------------------------------------------------------- /guide/service-worker-precache.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Service Worker Precache | Guide 3 | --- 4 | 5 | # Service Worker Precache 6 | 7 | As explained in the [Service Worker](/guide/#service-worker) section, service workers act as proxies intercepting requests between the browser and the server. 8 | 9 | To add PWA capability to your application, we need to give it a service worker. The service worker's precache manifest must include all the resources of your application, so that the service worker knows what resources to download into the browser's cache storage for use during `network requests interception` and when the application is offline. 10 | 11 | ::: tip Network requests interception 12 | You can also configure whether to apply network request interception for any of your application resources. You can find more information on [Workbox - Caching Strategies](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#caching-strategies). 13 | ::: 14 | 15 | Once the application registers the service worker, the browser will try to install it. This involves downloading all the resources in the service worker's precache manifest, and then trying to activate the service worker to take the control of the application. 16 | 17 | ::: tip 18 | The browser will **only** download the resources in the service worker's precache manifest **if the service worker is not installed** (the first time the user visits your application) or **if there is a new version of your application** (if you change some resource in your application, the service worker will also change once you build the application, since its precache manifest is modified to include your changes). 19 | 20 | The browser will always download these resouces **in a background thread** and not in the main browser thread, so that the application is usable even before the service worker is installed. 21 | 22 | You can see this behaviour on this website or the [VueUse docs site](https://vueuse.org/) in a private window. Just open `Network Tab` on dev tools before visiting the site: the browser will be downloading all the resources while you navigate the site. 23 | ::: 24 | 25 | ## Precache Manifest 26 | 27 | Since `vite-plugin-pwa` plugin uses the [workbox-build](https://developer.chrome.com/docs/workbox/modules/workbox-build/) node library to build the service worker, it will only include `css`, `js` and `html` resources in the manifest precache (check the `globPatterns` entry in [GlobPartial](https://developer.chrome.com/docs/workbox/modules/workbox-build#type-GlobPartial)). 28 | 29 | The `workbox-build` node library is file based: it will traverse the build output folder of your application. `Vite` will generate your build in the `dist` folder, and so, `workbox-build` will traverse the `dist` folder adding all resources found in it to the service worker's precache manifest. 30 | 31 | If you need to include another resource types, you will need to add them to the `globPatterns` entry. Depending on the `strategy` configured in the `vite-plugin-pwa` plugin configuration, you will need to add it under the `workbox` or `injectManifest` configuration option. 32 | 33 | You can find more information in the [Static assets handling](/guide/static-assets) section. 34 | 35 | For example, if you need to add `ico`, `png` and `svg` resources in the example from the [Configuring vite-plugin-pwa - Guide](/guide/#configuring-vite-plugin-pwa) section, you will need to add `globPatterns` under `workbox` entry, since we're using the default `vite-plugin-pwa` strategy (`generateSW`): 36 | ```ts 37 | import { VitePWA } from 'vite-plugin-pwa' 38 | 39 | export default defineConfig({ 40 | plugins: [ 41 | VitePWA({ 42 | registerType: 'autoUpdate', 43 | workbox: { 44 | globPatterns: ['**/*.{js,css,html,ico,png,svg}'] 45 | } 46 | }) 47 | ] 48 | }) 49 | ``` 50 | -------------------------------------------------------------------------------- /guide/service-worker-strategies-and-behaviors.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Service Worker Strategies And Behaviors | Guide 3 | --- 4 | 5 | # Service Worker Strategies And Behaviors 6 | 7 | A service worker strategy is related to how the `vite-plugin-pwa` plugin will generate your service worker, while the behavior of a service worker is related to how the service worker will work in the browser once the browser detects a new version of your application. 8 | 9 | ## Service Worker Strategies 10 | 11 | As we mention in [Configuring vite-plugin-pwa](/guide/#configuring-vite-plugin-pwa) section, `vite-plugin-pwa` plugin will use `workbox-build` node library to generate your service worker. There are 2 available strategies, `generateSW` and `injectManifest`: 12 | - `generateSW`: the `vite-plugin-pwa` will generate the service worker for you, you don't need to write the code for the service worker 13 | - `injectManifest`: the `vite-plugin-pwa` plugin will compile your custom service worker and inject its precache manifest 14 | 15 | To configure the service worker strategy, use the `strategies`' plugin option with `generateSW` (**default strategy**) or `injectManifest` value. 16 | 17 | You can find more information about the strategies in the [generateSW](/workbox/generate-sw) or [injectManifest](/workbox/inject-manifest) `Workbox` sections. 18 | 19 | ## Service Worker Behaviors 20 | 21 | The behavior of the service worker will help you to update the application in the browser, that is, when the browser detects a new version of your application, you can control how the browser updates it. 22 | 23 | You may want to not bother users and just have the browser update the application when there is a new version: the user will only see a reload of the page they are on. 24 | 25 | Or you may want to inform the user that there is a new version of the application, and let the user decide when to update it: simply because you want it to behave that way or because your application requires it (for example, to prevent data loss if the user is filling out a form). 26 | 27 | To configure the service worker behavior, use the `registerType` plugin option with `autoUpdate` or `prompt` (**default strategy**) value. 28 | 29 | You can find more information about the behaviors in the [auto-update](/guide/auto-update) or [prompt-for-update](/guide/prompt-for-update) sections for `generateSW` strategy or in [inject-manifest](/guide/inject-manifest) section for `injectManifest` strategy. 30 | -------------------------------------------------------------------------------- /guide/service-worker-without-pwa-capabilities.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Service Worker without PWA capabilities | Guide 3 | --- 4 | 5 | # Service Worker without PWA capabilities 6 | 7 | Sometimes you don't need the full blown PWA functionality like **offline cache** and **manifest file**, but need simple custom Service Worker. 8 | 9 | You can disable all `vite-plugin-pwa` supported features, and use it just to manage your Service Worker file. 10 | 11 | ## Service Worker code 12 | 13 | Suppose you want to have a Service Worker file that captures browser `fetch`: 14 | ```js 15 | // src/service-worker.js or src/service-worker.ts 16 | self.addEventListener("fetch", (event) => { 17 | event.respondWith(fetch(event.request)); 18 | }); 19 | ``` 20 | 21 | You would like to have this service worker reloaded on each change in **development** and prepared for **production**. 22 | 23 | ## Plugin Configuration 24 | 25 | You should configure `vite-plugin-pwa` plugin options in your Vite configuration file with the following options: 26 | ```js 27 | // vite.config.js or vite.config.ts 28 | VitePWA({ 29 | srcDir: "src", 30 | filename: "service-worker.js", 31 | strategies: "injectManifest", 32 | injectRegister: false, 33 | manifest: false, 34 | injectManifest: { 35 | injectionPoint: undefined, 36 | }, 37 | }) 38 | ``` 39 | 40 | ## Development 41 | 42 | If you would like the service worker to run in development, make sure to enable it in the [devOptions](/guide/development#plugin-configuration) and to set the type to [module](/guide/development#injectmanifest-strategy) if required. 43 | 44 | ## Registering of the Service Worker in your app 45 | 46 | Use the code below in your entry point module: 47 | ```js 48 | // src/main.js or src/main.ts 49 | if ('serviceWorker' in navigator) { 50 | navigator.serviceWorker.register( 51 | import.meta.env.MODE === 'production' ? '/service-worker.js' : '/dev-sw.js?dev-sw' 52 | ) 53 | } 54 | ``` 55 | 56 | If you're using import statements inside your service worker (will work only on chromium based browsers) check [injectManifest](/guide/development.html#injectmanifest-strategy) section for more info: 57 | ```js 58 | // src/main.js or src/main.ts 59 | if ('serviceWorker' in navigator) { 60 | navigator.serviceWorker.register( 61 | import.meta.env.MODE === 'production' ? '/service-worker.js' : '/dev-sw.js?dev-sw', 62 | { type: import.meta.env.MODE === 'production' ? 'classic' : 'module' } 63 | ) 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /guide/static-assets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Static assets handling | Guide 3 | --- 4 | 5 | # Static assets handling 6 | 7 | By default, all icons on `PWA Web App Manifest` option found under Vite's `publicDir` option directory, will be included in the service worker *precache*. You can disable this option using `includeManifestIcons: false`. 8 | 9 | You can also add other static assets such as `favicon`, `svg` and `font` files using `includeAssets` option. The `includeAssets` option will be resolved using [tinyglobby](https://github.com/SuperchupuDev/tinyglobby) found under Vite's `publicDir` option directory, and so you can use regular expressions to include those assets, for example: `includeAssets: ['fonts/*.ttf', 'images/*.png']`. You don't need to configure `PWA Manifest icons` on `includeAssets` option. 10 | 11 | ## Reusing src/assets images 12 | 13 | ::: warning 14 | This feature is not yet available. 15 | ::: 16 | 17 | If you are using images in your application via `src/assets` directory (or any other directory), and you want to reuse those images in your `PWA Manifest` icons, you can use them with these 3 limitations: 18 | - any image under `src/assets` directory (or any other directory) must be used in your application via static import or directly on the `src` attribute 19 | - you must reference the images in the `PWA Manifest` icons using the assets directory path relative to the root folder: `./src/assets/logo.png` or `src/assets/logo.png` 20 | - inlined icons cannot be used, in that case you will need to copy/move those images to the Vite's `publicDir` option directory: refer to [Importing Asset as URL](https://vitejs.dev/guide/assets.html#importing-asset-as-url) and [Vite's assetsInlineLimit option](https://vitejs.dev/config/build-options.html#build-assetsinlinelimit) 21 | 22 | 23 | ::: warning 24 | If you're using `PWA Manifest` icons from any asset folder, but you are not using those images in your application (via static import or in src attribute), Vite will not emit those assets, and so missing from the build output: 25 | 26 | ```shell 27 | Error while trying to use the following icon from the Manifest: https://localhost/src/assets/pwa-192x192.png (Download error or resource isn't a valid image) 28 | ``` 29 | 30 | In that case, you need to copy or move those images to the Vite's `publicDir` option directory (defaults to `public`) and configure the icons properly. 31 | ::: 32 | 33 | For example, if you have the following image `src/assets/logo-192x192.png` you can add it to your `PWA Manifest` icon using: 34 | 35 | ```json 36 | { 37 | "src": "./src/assets/logo-192x192.png", 38 | "sizes": "192x192", 39 | "type": "image/png" 40 | } 41 | ``` 42 | 43 | then, in your codebase, you must use it via static import: 44 | 45 | ```js 46 | // src/main.js or src/main.ts 47 | // can be any js/ts/jsx/tsx module or single file component 48 | import logo from './assets/logo-192x192.png' 49 | 50 | document.getElementById('logo-img').src = logo 51 | ``` 52 | 53 | or using the `src` attribute: 54 | 55 | ```js 56 | // src/main.js or src/main.ts 57 | // can be any js/ts/jsx/tsx module or single file component 58 | document.getElementById('#app').innerHTML = ` 59 | Logo 60 | ` 61 | ``` 62 | 63 | ## globPatterns 64 | 65 | If you need to include other assets that are not under Vite's `publicDir` option directory, you can use the `globPatterns` parameter of [workbox](https://developer.chrome.com/docs/workbox/modules/workbox-build#generatesw) or [injectManifest](https://developer.chrome.com/docs/workbox/modules/workbox-build#injectmanifest) plugin options. 66 | 67 | ::: warning 68 | If you configure `globPatterns` on `workbox` or `injectManifest` plugin option, you **MUST** include all your assets patterns: `globPatterns` will be used by `workbox-build` to match files on `dist` folder. 69 | 70 | By default, `globPatterns` will be `**/*.{js,css,html}`: `workbox` will use [glob primer](https://github.com/isaacs/node-glob#glob-primer) to match files using `globPatterns` as filter. 71 | 72 | A common pitfall is to only include some assets and forget to add `css`, `js` and `html` assets pattern, and then your service worker will complain about missing resources. 73 | 74 | For example, if you don't include `html` assets pattern, you will get this error from your service worker: **WorkboxError non-precached-url index.html**. 75 | ::: 76 | 77 | To configure `globPatterns` you need to use `workbox` or `injectManifest` plugin option for`generateSW` and `injectManifest` strategies respectively: 78 | 79 | ::: code-group 80 | ```ts [generateSW] 81 | VitePWA({ 82 | workbox: { 83 | globPatterns: ['**/*.{js,css,html}'], 84 | } 85 | }) 86 | ``` 87 | ```ts [injectManifest] 88 | VitePWA({ 89 | injectManifest: { 90 | globPatterns: ['**/*.{js,css,html}'], 91 | } 92 | }) 93 | ``` 94 | ::: 95 | 96 | -------------------------------------------------------------------------------- /guide/testing-service-worker.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Testing Service Worker | Guide 3 | --- 4 | 5 | # Testing Service Worker 6 | 7 | There are quite a few test libraries, `vite-plugin-pwa` uses [Vitest](https://vitest.dev/) for build testing and [Playwright](https://playwright.dev/) for in-browser testing (with the Chromium browser only). 8 | 9 | You can check any framework example in the `examples` folder in the corresponding repo: 10 | - [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa/tree/main/examples) 11 | - [@vite-pwa/nuxt](https://github.com/vite-pwa/nuxt) (in root folder) 12 | - [@vite-pwa/sveltekit](https://github.com/vite-pwa/sveltekit/tree/main/examples) 13 | 14 | and the corresponding contributing guide: 15 | - [running tests in vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa/blob/main/CONTRIBUTING.md#running-tests) 16 | - [running tests in @vite-pwa/nuxt](https://github.com/vite-pwa/nuxt/blob/main/CONTRIBUTING.md#running-tests) 17 | - [running tests in @vite-pwa/sveltekit](https://github.com/vite-pwa/sveltekit/blob/main/CONTRIBUTING.md#running-tests) 18 | 19 | `vite-plugin-pwa` and `@vite-pwa/nuxt` have been added to the [Vite ecosystem-ci](https://github.com/vitejs/vite-ecosystem-ci) and [Nuxt ecosystem-ci](https://github.com/nuxt/ecosystem-ci) respectively to detect possible regressions in new Vite/Nuxt versions: 20 | - [Discord Vite ecosystem-ci](https://discord.com/channels/804011606160703521/928398470086291456) 21 | - [Discord Nuxt ecosystem-ci](https://discord.com/channels/473401852243869706/1098558476483055656) 22 | 23 | We're also working to include `@vite-pwa/sveltekit` in the [Svelte ecosystem-ci](https://github.com/sveltejs/svelte-ecosystem-ci). 24 | 25 | ## Testing build 26 | 27 | Check `vitest.config.mts` in the root folder and the `test` folder in each example. 28 | 29 | You have a `test` script in each example `package.json` file to run build and in-browser tests. 30 | 31 | ## Testing in-browser 32 | 33 | Check `playwright.config.ts` in the root folder and the `client-test` folder in each example. 34 | 35 | You have a `test` script in each example `package.json` file to run build and in-browser tests. 36 | 37 | In this case, we also need to start a server to run the tests, check `webServer` in `playwright.config.ts`. 38 | -------------------------------------------------------------------------------- /guide/unregister-service-worker.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Unregister Service Worker | Guide 3 | --- 4 | 5 | # Unregister Service Worker 6 | 7 | If you want to unregister the service worker from your PWA application, you only need to add `selfDestroying: true` to the plugin configuration. 8 | 9 | `vite-plugin-pwa` plugin will create a new special service worker and replace the existing one in your application once deployed in production: it has to be put in the place of the previous broken/unwanted service worker, with the same name. 10 | 11 | ::: info 12 | From version `0.17.2+`, the service worker will delete all of its cache storage entries. 13 | ::: 14 | 15 | ::: danger 16 | It is **IMPORTANT TO NOT CHANGE ANYTHING** in the plugin configuration, especially **DO NOT CHANGE THE SERVICE WORKER NAME**, just keep the options and the PWA UI components (if included), the plugin will take care of changing the service worker and avoid interacting with the UI if configured. 17 | ::: 18 | 19 | In a future, if you want to add the PWA again to your application, you only need to remove the `selfDestroying` option or just disable it: `selfDestroying: false`. 20 | 21 | ## Custom `selfDestroying` Service Worker 22 | 23 | If you want to remove the current deployed service worker but installing a new one, don't use `selfDestroying`: 24 | - create a new JavaScript file with the current deployed service worker name in the `public` folder, check the example below 25 | - change `filename` in the PWA configuration (this will generate a new service worker with the new name) 26 | 27 | For example, if you don't specify the `filename`, the service worker name will be `sw.js` (default). Change the `filename` PWA option to `service-worker.js` or other name different to `sw.js`, then add the following code to `public/sw.js` file (the current deployed service worker): 28 | 29 | ```js 30 | // public/sw.js 31 | self.addEventListener('install', (e) => { 32 | self.skipWaiting(); 33 | }); 34 | self.addEventListener('activate', (e) => { 35 | self.registration.unregister() 36 | .then(() => self.clients.matchAll()) 37 | .then((clients) => { 38 | clients.forEach((client) => { 39 | if (client instanceof WindowClient) 40 | client.navigate(client.url); 41 | }); 42 | return Promise.resolve(); 43 | }) 44 | .then(() => { 45 | self.caches.keys().then((cacheNames) => { 46 | Promise.all( 47 | cacheNames.map((cacheName) => { 48 | return self.caches.delete(cacheName); 49 | }) 50 | ); 51 | }) 52 | }); 53 | }); 54 | ``` 55 | 56 | You can repeat the above process as many times as necessary, **remember not to delete** any service worker from the public directory (you don't know what version the users of your application have installed). 57 | 58 | ## Development 59 | 60 | You can also check the `selfDestroying` plugin option in the dev server with development options enabled: check [Development section](/guide/development) for more info. 61 | 62 | ## Examples 63 | 64 | You have in the examples folder the `**-destroy` scripts in their corresponding `package.json`, you can try it on the development server or from the production build. 65 | 66 | ## Credits 67 | 68 | The implementation is based on this GitHub repo [Self-destroying ServiceWorker](https://github.com/NekR/self-destroying-sw), for more info read [Medium: Self-destroying ServiceWorker](https://medium.com/@nekrtemplar/self-destroying-serviceworker-73d62921d717). 69 | -------------------------------------------------------------------------------- /hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/hero.png -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | sidebar: false 4 | 5 | title: Vite PWA 6 | 7 | hero: 8 | name: PWA 9 | text: Vite Plugin 10 | tagline: | 11 | PWA integrations for Vite and the ecosystem 12 | Zero-config and framework-agnostic 13 | PWA Plugin for Vite 14 | image: 15 | light: 16 | src: /icon_light.svg 17 | alt: Vite PWA Logo 18 | dark: 19 | src: /icon_dark.svg 20 | alt: Vite PWA Logo 21 | actions: 22 | - theme: brand 23 | text: Get Started 24 | link: /guide/ 25 | - theme: alt 26 | text: View on GitHub 27 | link: https://github.com/vite-pwa/ 28 | 29 | features: 30 | - icon: 👌 31 | title: Zero-Config 32 | details: Sensible built-in default configs for common use cases 33 | - icon: 🔩 34 | title: Extensible 35 | details: Expose the full ability to customize the behavior of the plugin 36 | - icon: 🔌 37 | title: Offline Support 38 | details: Generate Service Worker with Offline support (via Workbox) 39 | - icon: ⚡ 40 | title: Fully tree shakable 41 | details: Auto inject Web App Manifest 42 | - icon: 💬 43 | title: Prompt for new content 44 | details: Built-in support for Vanilla JavaScript, Vue 3, React, Svelte, SolidJS and Preact 45 | link: /guide/prompt-for-update 46 | linkText: Prompt for new content refreshing 47 | - icon: ⚙️ 48 | title: Stale-while-revalidate 49 | details: Automatic reload when new content is available 50 | - icon: ✨ 51 | title: Static assets handling 52 | details: Configure static assets for offline support 53 | - icon: 🐞 54 | title: Development Support 55 | details: Debug your custom service worker logic as you develop your application 56 | link: /guide/development 57 | linkText: Development 58 | - icon: 🛠️ 59 | title: Versatile 60 | details: 'Integration with meta frameworks: îles, SvelteKit, VitePress, Astro, Nuxt 3, and Remix' 61 | link: /frameworks/ 62 | linkText: Frameworks 63 | - icon: 💥 64 | title: PWA Assets Generator 65 | details: Generate all the PWA assets from a single command and a single source image 66 | link: /assets-generator/ 67 | linkText: Getting Started 68 | - icon: 🚀 69 | title: PWA Assets Integration 70 | details: Serve, generate and inject PWA Assets on the fly in your application 71 | link: /assets-generator/integrations 72 | linkText: PWA Assets Integrations 73 | --- 74 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NODE_VERSION = "18" 3 | 4 | [build] 5 | publish = ".vitepress/dist" 6 | command = "pnpm run build" 7 | 8 | [[headers]] 9 | for = "/manifest.webmanifest" 10 | [headers.values] 11 | Content-Type = "application/manifest+json" 12 | 13 | [[headers]] 14 | for = "/vite-plugin-pwa.excalidraw" 15 | [headers.values] 16 | Content-Type = "application/json; charset=UTF-8" 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-pwa-docs", 3 | "type": "module", 4 | "version": "0.21.1", 5 | "packageManager": "pnpm@9.14.4", 6 | "description": "Zero-config PWA for Vite Documentation", 7 | "author": "antfu ", 8 | "license": "MIT", 9 | "funding": "https://github.com/sponsors/antfu", 10 | "homepage": "https://github.com/vite-pwa/docs#readme", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/vite-pwa/docs.git" 14 | }, 15 | "bugs": "https://github.com/vite-pwa/docs/issues", 16 | "keywords": [ 17 | "react", 18 | "pwa", 19 | "vue", 20 | "vitepress", 21 | "preact", 22 | "svelte", 23 | "sveltekit", 24 | "workbox", 25 | "solidjs", 26 | "vite", 27 | "vite-plugin", 28 | "astro", 29 | "astro-integration", 30 | "iles", 31 | "nuxt", 32 | "nuxt module" 33 | ], 34 | "engines": { 35 | "node": ">=16.0.0" 36 | }, 37 | "scripts": { 38 | "dev": "vitepress dev", 39 | "build": "vitepress build", 40 | "preview": "nr build && vitepress serve", 41 | "https": "nr build && serve .vitepress/dist", 42 | "lint": "eslint .", 43 | "lint-fix": "nr lint --fix", 44 | "generate-pwa-icons": "pwa-assets-generator" 45 | }, 46 | "dependencies": { 47 | "@vueuse/core": "^11.1.0", 48 | "@vueuse/shared": "^11.1.0", 49 | "vue": "^3.5.6" 50 | }, 51 | "devDependencies": { 52 | "@antfu/eslint-config": "^0.43.1", 53 | "@antfu/ni": "^0.21.12", 54 | "@typescript-eslint/eslint-plugin": "^5.62.0", 55 | "@typescript-eslint/parser": "^5.62.0", 56 | "@vite-pwa/assets-generator": "^0.2.6", 57 | "@vite-pwa/vitepress": "^0.5.3", 58 | "@vitejs/plugin-vue": "^5.1.4", 59 | "@vueuse/core": "^11.1.0", 60 | "eslint": "^8.54.0", 61 | "https-localhost": "^4.7.1", 62 | "typescript": "^5.4.5", 63 | "unocss": "^0.62.4", 64 | "unplugin-vue-components": "^0.27.4", 65 | "vite-plugin-pwa": "^0.21.1", 66 | "vitepress": "^1.5.0", 67 | "vitepress-plugin-group-icons": "^1.3.0", 68 | "workbox-window": "^7.3.0" 69 | }, 70 | "pnpm": { 71 | "peerDependencyRules": { 72 | "ignoreMissing": [ 73 | "@algolia/client-search" 74 | ] 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /public/Cascadia.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/Cascadia.woff2 -------------------------------------------------------------------------------- /public/Virgil.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/Virgil.woff2 -------------------------------------------------------------------------------- /public/_headers: -------------------------------------------------------------------------------- 1 | / 2 | X-Frame-Options: DENY 3 | X-XSS-Protection: 1; mode=block 4 | 5 | /guide/ 6 | X-Frame-Options: DENY 7 | X-XSS-Protection: 1; mode=block 8 | 9 | /frameworks/ 10 | X-Frame-Options: DENY 11 | X-XSS-Protection: 1; mode=block 12 | 13 | /examples/ 14 | X-Frame-Options: DENY 15 | X-XSS-Protection: 1; mode=block 16 | 17 | /deployment/ 18 | X-Frame-Options: DENY 19 | X-XSS-Protection: 1; mode=block 20 | 21 | /workbox/ 22 | X-Frame-Options: DENY 23 | X-XSS-Protection: 1; mode=block 24 | 25 | /*.html 26 | X-Frame-Options: DENY 27 | X-XSS-Protection: 1; mode=block 28 | 29 | /* 30 | X-Content-Type-Options: nosniff 31 | Referrer-Policy: no-referrer 32 | Strict-Transport-Security: max-age=31536000; includeSubDomains 33 | 34 | /assets/* 35 | cache-control: max-age=31536000 36 | cache-control: immutable 37 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 11 | 21 | 22 | -------------------------------------------------------------------------------- /public/icon_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/icon_gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/icon_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/integration-logos/astro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/integration-logos/iles.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/integration-logos/nuxt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/integration-logos/remix.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/integration-logos/sveltekit.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/integration-logos/vitepress.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /public/maskable-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/maskable-icon.png -------------------------------------------------------------------------------- /public/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/og-image.png -------------------------------------------------------------------------------- /public/prompt-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/prompt-update.png -------------------------------------------------------------------------------- /public/pwa-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/pwa-192x192.png -------------------------------------------------------------------------------- /public/pwa-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/pwa-512x512.png -------------------------------------------------------------------------------- /public/pwa-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/pwa-64x64.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | -------------------------------------------------------------------------------- /public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | image/svg+xml 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/shortcuts/assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/shortcuts/assets.png -------------------------------------------------------------------------------- /public/shortcuts/assets.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/shortcuts/deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/shortcuts/deploy.png -------------------------------------------------------------------------------- /public/shortcuts/deploy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/shortcuts/frameworks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/shortcuts/frameworks.png -------------------------------------------------------------------------------- /public/shortcuts/frameworks.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/shortcuts/guide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/shortcuts/guide.png -------------------------------------------------------------------------------- /public/shortcuts/guide.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 11 | 21 | 22 | -------------------------------------------------------------------------------- /public/shortcuts/workbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/shortcuts/workbox.png -------------------------------------------------------------------------------- /public/shortcuts/workbox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/team-avatars/antfu.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/team-avatars/antfu.webp -------------------------------------------------------------------------------- /public/team-avatars/hannoeru.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/team-avatars/hannoeru.webp -------------------------------------------------------------------------------- /public/team-avatars/userquin.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vite-pwa/docs/fb4e96eaf8b5c1ebd72a0f799042afafc4c5065e/public/team-avatars/userquin.webp -------------------------------------------------------------------------------- /pwa-assets.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | minimal2023Preset as preset, 4 | } from '@vite-pwa/assets-generator/config' 5 | 6 | export default defineConfig({ 7 | overrideAssets: false, 8 | logLevel: 'info', 9 | headLinkOptions: { 10 | preset: '2023', 11 | }, 12 | preset: { 13 | ...preset, 14 | assetName(type, size) { 15 | switch (type) { 16 | case 'transparent': 17 | return `pwa-${size.width}x${size.height}.png` 18 | case 'maskable': 19 | return 'maskable-icon.png' 20 | case 'apple': 21 | return 'apple-touch-icon.png' 22 | } 23 | }, 24 | }, 25 | images: ['public/favicon.svg'], 26 | }) 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": false, 4 | "target": "ESNext", 5 | "lib": ["DOM", "ESNext"], 6 | "module": "esnext", 7 | "moduleResolution": "node", 8 | "baseUrl": ".", 9 | "paths": { 10 | "~/*": ["src/*"] 11 | }, 12 | "types": [ 13 | "@types/fs-extra", 14 | "node", 15 | "vite/client", 16 | "vite-plugin-pwa/vanillajs", 17 | "vitepress" 18 | ], 19 | "resolveJsonModule": true, 20 | "esModuleInterop": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "noUnusedLocals": true, 25 | "skipLibCheck": true 26 | }, 27 | "include": [ 28 | "./*.ts", 29 | "./.vitepress/**/*.ts", 30 | "./.vitepress/**/*.vue" 31 | ], 32 | "exclude": ["dist", "node_modules"] 33 | } 34 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { defineConfig } from 'vite' 3 | import Components from 'unplugin-vue-components/vite' 4 | import { presetAttributify, presetUno } from 'unocss' 5 | import Unocss from 'unocss/vite' 6 | 7 | export default defineConfig({ 8 | logLevel: 'info', 9 | optimizeDeps: { 10 | exclude: [ 11 | '@vueuse/core', 12 | 'vitepress', 13 | ], 14 | }, 15 | server: { 16 | hmr: { 17 | overlay: false, 18 | }, 19 | }, 20 | resolve: { 21 | alias: [ 22 | { 23 | find: /^.*\/VPNavBarTitle\.vue$/, 24 | replacement: fileURLToPath( 25 | new URL('./.vitepress/theme/components/vp/NavBarTitle.vue', import.meta.url), 26 | ), 27 | }, 28 | { 29 | find: /^.*\/VPTeamMembersItem\.vue$/, 30 | replacement: fileURLToPath( 31 | new URL('./.vitepress/theme/components/vp/TeamMembersItem.vue', import.meta.url), 32 | ), 33 | }, 34 | ], 35 | }, 36 | plugins: [ 37 | // https://github.com/antfu/vite-plugin-components 38 | Components({ 39 | dirs: [ 40 | '.vitepress/theme/components', 41 | ], 42 | // allow auto load markdown components under `./src/components/` 43 | extensions: ['vue', 'md'], 44 | 45 | // allow auto import and register components used in markdown 46 | include: [/\.vue$/, /\.vue\?vue/, /\.md$/], 47 | 48 | // generate `components.d.ts` for ts support with Volar 49 | dts: '.vitepress/components.d.ts', 50 | }), 51 | 52 | // https://github.com/unocss/unocss 53 | Unocss({ 54 | presets: [presetUno(), presetAttributify()], 55 | }), 56 | ], 57 | }) 58 | -------------------------------------------------------------------------------- /workbox/generate-sw.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: generateSW | Workbox 3 | --- 4 | 5 | # generateSW 6 | 7 | You must read [Which Mode to Use](https://developer.chrome.com/docs/workbox/modules/workbox-build/#which-mode-to-use) before decide using this strategy on `vite-plugin-pwa` plugin. 8 | 9 | You can find the documentation for this method on `workbox` site: [generateSW](https://developer.chrome.com/docs/workbox/modules/workbox-build#method-generateSW). 10 | 11 | You can find a guide for plugins on `workbox` site: [Using Plugins](https://developer.chrome.com/docs/workbox/using-plugins/). 12 | 13 | ## Cache External Resources 14 | 15 | If you use some `CDN` to download some resources like `fonts` and `css`, you must include them into the service worker precache, and so your application will work when offline. 16 | 17 | The following example will use `css` from `https://fonts.googleapis.com` and `fonts` from `https://fonts.gstatic.com`. 18 | 19 | On `index.html` file you must configure the `css` `link`, you **MUST** also include `crossorigin="anonymous"` attribute for the external resources (see [Handle Third Party Requests](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime#cross-origin_considerations)): 20 | 21 | ::: details index.html 22 | ```html 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | ``` 31 | ::: 32 | 33 | Then on your `vite.config.ts` file add the following code: 34 | 35 | ::: details VitePWA options 36 | ```ts 37 | VitePWA({ 38 | workbox: { 39 | runtimeCaching: [ 40 | { 41 | urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i, 42 | handler: 'CacheFirst', 43 | options: { 44 | cacheName: 'google-fonts-cache', 45 | expiration: { 46 | maxEntries: 10, 47 | maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days 48 | }, 49 | cacheableResponse: { 50 | statuses: [0, 200] 51 | } 52 | } 53 | }, 54 | { 55 | urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i, 56 | handler: 'CacheFirst', 57 | options: { 58 | cacheName: 'gstatic-fonts-cache', 59 | expiration: { 60 | maxEntries: 10, 61 | maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days 62 | }, 63 | cacheableResponse: { 64 | statuses: [0, 200] 65 | }, 66 | } 67 | } 68 | ] 69 | } 70 | }) 71 | ``` 72 | ::: 73 | 74 | ## Exclude routes 75 | 76 | To exclude some routes from being intercepted by the service worker, you just need to add those routes using a `regex` list to the `navigateFallbackDenylist` option of `workbox`: 77 | 78 | ```ts 79 | VitePWA({ 80 | workbox: { 81 | navigateFallbackDenylist: [/^\/backoffice/] 82 | } 83 | }) 84 | ``` 85 | 86 | ::: warning 87 | You must deal with offline support for excluded routes: if requesting a page excluded on `navigateFallbackDenylist` you will get `No internet connection`. 88 | ::: 89 | 90 | ## Background Sync 91 | 92 | You can add this code to the plugin on your `vite.config.ts` file to add a `Background Sync` manager to your service worker: 93 | 94 | ::: details VitePWA options 95 | ```ts 96 | VitePWA({ 97 | workbox: { 98 | runtimeCaching: [{ 99 | handler: 'NetworkOnly', 100 | urlPattern: /\/api\/.*\/*.json/, 101 | method: 'POST', 102 | options: { 103 | backgroundSync: { 104 | name: 'myQueueName', 105 | options: { 106 | maxRetentionTime: 24 * 60 107 | } 108 | } 109 | } 110 | }] 111 | } 112 | }) 113 | ``` 114 | ::: 115 | -------------------------------------------------------------------------------- /workbox/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started | Workbox 3 | prev: 4 | text: Apache Http Server 2.4+ | Deployment 5 | link: /deployment/apache 6 | --- 7 | 8 | # Getting Started 9 | 10 | [**Workbox**](https://developer.chrome.com/docs/workbox/) is a massive package with many modules to make service worker development more enjoyable and remove the need to deal with the low-level service worker API. 11 | 12 | In this document, we focus only on the [workbox-build](https://developer.chrome.com/docs/workbox/modules/workbox-build) module from **Workbox**. 13 | 14 | :::warning 15 | From version `0.16.0`, `vite-plugin-pwa` has been updated to use latest `workbox` version `7.0.0` that requires Node 16 or above. 16 | ::: 17 | 18 | :::tip 19 | From version `0.20.2`, the plugin will throw an error if the `maximumFileSizeToCacheInBytes` warning is present when building the service worker. 20 | ::: 21 | 22 | ## workbox-build module 23 | 24 | This module is for build process purposes (a `node` module); that is, `Vite Plugin PWA` will use it to build your service-worker. 25 | 26 | We focus on 2 methods of this module: 27 | - [generateSW](/workbox/generate-sw): for generating the service worker. 28 | - [injectManifest](/workbox/inject-manifest): for when you need more control over your service worker. 29 | 30 | You should read [Which Mode to Use](https://developer.chrome.com/docs/workbox/modules/workbox-build/#which-mode-to-use) before deciding which strategy to use. 31 | 32 | In short, the `generateSW` function abstracts away the need to work directly with the service worker API when building the service worker. This method can be configured using plugins instead of writing your own service worker code (`generateSW` will generate the code for you). 33 | 34 | While the `injectManifest` method will use your existing service worker and build/compile it. 35 | 36 | ## How is `workbox-build` related to `vite-plugin-pwa`? 37 | 38 | `vite-plugin-pwa` uses `generateSW` and `injectManifest` Workbox methods internally when the `strategies` option is set to `generateSW` and `injectManifest` respectively. 39 | 40 | When you configure `strategies: 'generateSW'` option (the default value) in your `vite.config.*` file, the plugin invokes workbox' `generateSW` method. The options passed to the `workbox-build` method will be those provided via the `workbox` option of the plugin configuration. 41 | 42 | When you configure `strategies: 'injectManifest'` option, the plugin will first build your custom service worker via custom `Vite` build. With the build result, vite-plugin-pwa will call Workbox's `injectManifest` method passing those options provided via the `injectManifest` option of the plugin configuration. 43 | --------------------------------------------------------------------------------