Cover

Using Vite's Plugin for Progressive Web Apps (PWAs)

I’ve worked with Progressive Web Application plug-ins with several SPA frameworks. Most of them are pretty simple to implement. But when I learned about Vite’s plug-in, I was intrigued since that would work across different SPA frameworks. Let’s take a look at it.

I also made a Coding Short video that covers this same topic, if you’d rather watch than read:

The Vite plug-in for PWA works at the Vite/Build level, not for your specific framework (or lack of a framework). That means it will work for Vue, React, SvelteKit and Vanilla JS (and any other Vite-powered development). Before we do any of this, we have a working website:

A example website

To install it, you just need to add it to your development-time dependencies:

> npm i vite-plugin-pwa --save-dev

Once installed, you can add it to your vite.config.js file:

...
import { VitePWA } from "vite-plugin-pwa";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    VitePWA()
  ],
  ...
})

With this installed, you’ll see that your builds will generate some extra files:

build started...
✓ 30 modules transformed.
../wwwroot/registerSW.js               0.13 kB
../wwwroot/manifest.webmanifest        0.14 kB
../wwwroot/index.html                  0.56 kB
../wwwroot/assets/index-cfd5afe3.css   7.14 kB │ gzip:  1.97 kB
../wwwroot/assets/index-25653f73.js   75.09 kB │ gzip: 30.04 kB
built in 1378ms.

PWA v0.14.1
mode      generateSW
precache  5 entries (80.98 KiB)
files generated
  ..\wwwroot\sw.js
  ..\wwwroot\workbox-519d0965.js

The file generated by the plug-in include:

  • manifest.webmanifest: Metadata about the app and an indication that it can be installed.
  • sw.js: A, required, service worker that supports running as an app (and offline).
  • registerSW.js: A new script that Vite injects into the index.html that registers the service worker.
  • workbox-*.js: Workbox specific code to support the PWA.

With this generated, you should see the “install icon” on supported browsers:

The Install Button on Chrome

You can customize the metadata that is used by just adding a metadata object in the plug-in:

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    VitePWA({      
      manifest: { 
        icons: [
          {
            src: "/icons/512.png",
            sizes: "512x512",
            type: "image/png",
            purpose: "any maskable"
          }
        ]
      }
    })],
...

The properties that you can customize in the manifest are all defined here.

If you run the example now, you can look at the manifest for errors or omissions:

Examining the Manifest

If you click on the Application tab in the tools, you can see that it is complaining about missing icons for different operating systems.

If you switch to the Service Worker, you can see it is running:

The Service Worker

But how does this work? The Service Worker can intercept network requests and serve the content necessary to load up the project. In fact, if you look at the “Cache Storage”, you’ll see the standard cache of the web page’s files:

workbox Cache

The feature of the browser that supports all of this is called workbox, so if you look at that cache, you’ll see the files that are being cached to load this offline (including .html, .js, .css, etc.). So, let’s try and making the app offline to see what happens:

workbox Cache

You can go offline in the Network tab by changing the networking to offline. If you refresh the page, you’ll get something that looks like this:

Offline Version of Site

But what happened? The cache (see earlier) is only caching the files needed to serve the page, not for any functionality.

How do we fix this? Luckily, the plug-in supports changing the workbox settings to create your own caches (called runtimeCaching). To do this we return back to the vite.config.js file:

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    VitePWA({
      manifest: {
        icons: [
          {
            src: "/icons/512.png",
            sizes: "512x512",
            type: "image/png",
            purpose: "any maskable",
          },
        ],
      },
      workbox: {
        runtimeCaching: [
          {
            urlPattern: ({ url }) => {
              return url.pathname.startsWith("/api");
            },
            handler: "CacheFirst" as const,
            options: {
              cacheName: "api-cache",
              cacheableResponse: {
                statuses: [0, 200],
              },
            },
          },
        ],
      },
    }),
  ],'
...

By creating a section for workbox, we can configure a number of things, but for us we want to create an API cache. You can see that i’m testing all requests for /api and caching all GETs into our own cache. By enabling this, the customers reappear. We can see (and interrogate) the cache in the Application tools:

The API Cache

This sort of all encompassing cache might not be realistic, but you could be caching non-volatile API calls. This isn’t a solution for handling offline changes. You can use application-specific code to write it to session or local storage.

I hope you’ve seen how the Vite PWA plug-in works anbd how you can use it to install your website as a local application!

You can find the example of the project here:

Module Example