Skip to main content
Version: v7

Progressive Web Apps in Vue

Making your Vue app a PWA with Vite

The two main requirements of a PWA are a Service Worker and a Web Application Manifest. While it's possible to add both of these to an app manually, we recommend using the Vite PWA Plugin instead.

To get started, install the vite-plugin-pwa package:

npm install -D vite-plugin-pwa

Next, update your vite.config.js or vite.config.ts file and add vite-plugin-pwa:

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

export default defineConfig({
plugins: [vue(), VitePWA({ registerType: 'autoUpdate' })],
});

This minimal configuration allows your application to generate the Web Application Manifest and Service Worker on build.

For more information on configuring the Vite PWA Plugin, see the Vite PWA "Getting Started" Guide.

See the Vite PWA "Deploy" Guide for information on how to deploy your PWA.

Making your Vue app a PWA with Vue CLI

note

As of Ionic CLI v7, Ionic Vue starter apps ship with Vite instead of Vue CLI. See Making your Vue app a PWA with Vite for Vite instructions.

The two main requirements of a PWA are a Service Worker and a Web Application Manifest. While it's possible to add both of these to an app manually, the Vue CLI has some utilities for adding this for you.

For existing projects, you can run the vue add command to install the PWA plugin for Vue.

vue add pwa
note

If you have changes already in place, be sure to commit them in Git.

Once this is completed, Vue's CLI will have created a new registerServiceWorker.ts file and imported it into our main.ts.

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
// Added by the CLI
import './registerServiceWorker';

createApp(App).use(router).mount('#app');

The registerServiceWorker.ts file will point to a service worker that the CLI will create at build time. Inside of here we can customize the experience users will have when the service worker detects an update, change in network connectivity, or receives an error.

import { register } from 'register-service-worker';

if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready() {
console.log(
'App is being served from cache by a service worker.\n' + 'For more details, visit https://goo.gl/AFskqB'
);
},
registered() {
console.log('Service worker has been registered.');
},
cached() {
console.log('Content has been cached for offline use.');
},
updatefound() {
console.log('New content is downloading.');
},
updated() {
console.log('New content is available; please refresh.');
},
offline() {
console.log('No internet connection found. App is running in offline mode.');
},
error(error) {
console.error('Error during service worker registration:', error);
},
});
}

The service worker that is generated is based on Workbox's webpack plugin, and by default is setup to use GenerateSW(). Meaning that at build time, Workbox will automatically generate a service worker cache for all the files it processes.

If you want to configure this and change the default behavior, checkout the PWA plugin docs on GitHub.

Manifest

In addition to the service worker, the Vue PWA plugin also is responsible for creating a manifest file for your app as well. By default, the CLI will generate a manifest that contains the following entries.

{
"name": "pwa-test",
"short_name": "pwa-test",
"theme_color": "#4DBA87",
"icons": [
{
"src": "./img/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "./img/icons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "./img/icons/android-chrome-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./img/icons/android-chrome-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"start_url": ".",
"display": "standalone",
"background_color": "#000000"
}

Be sure to update the icons in public/img/icons to match your own brand. If you wanted to customize the theme color or name, be sure to read the PWA plugin docs on GitHub.

Deploying

You can use various hosts like Firebase, Vercel, Netlify, or even Azure Static Web Apps. All will have similar setup processes that need to be completed. For this guide, Firebase will be used as the hosting example. In addition to this guide, the Vue CLI docs also have a guide on how to deploy to various providers.

Firebase

Firebase hosting provides many benefits for Progressive Web Apps, including fast response times thanks to CDNs, HTTPS enabled by default, and support for HTTP2 push.

First, if not already available, create the project in Firebase.

Next, in a Terminal, install the Firebase CLI:

npm install -g firebase-tools
note

If it's the first time you use firebase-tools, login to your Google account with firebase login command.

With the Firebase CLI installed, run firebase init within your Ionic project. The CLI prompts:

"Which Firebase CLI features do you want to set up for this folder?" Choose "Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys".

Create a new Firebase project or select an existing one.

"Select a default Firebase project for this directory:" Choose the project you created on the Firebase website.

"What do you want to use as your public directory?" Enter "dist".

note

Answering this next question will ensure that routing, hard reload, and deep linking work in the app:

Configure as a single-page app (rewrite all urls to /index.html)?" Enter "Yes".

"File build/index.html already exists. Overwrite?" Enter "No".

Set up automatic builds and deploys with Github? Enter "Yes".

For which GitHub repository would you like to set up a Github Workflow? Enter your project name.

Set up the workflow to run a build script before every deploy? Enter "Yes".

What script should be run before every deploy? Enter npm ci && npm run build.

Set up automatic deployment to your sites live channel when a PR is merged? Enter "Yes".

What is the name of the get hooked branch associated with your sites live channel? Enter your project's main branch name.

A firebase.json config file is generated, configuring the app for deployment.

The last thing needed is to make sure caching headers are being set correctly. To do this, add a headers snippet to the firebase.json file. The complete firebase.json looks like:

{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{
"source": "/**",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000"
}
]
},
{
"source": "precache-manifest.*.js",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache"
}
]
},
{
"source": "service-worker.js",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache"
}
]
}
]
}
}

For more information about the firebase.json properties, see the Firebase documentation.

Next, build an optimized version of the app by running:

ionic build

Last, deploy the app by running:

firebase deploy

After this completes, the app will be live.