Enable Offline Support to Websites with Workbox

Jason Thai

workbox

Recently I have added offline support for this blog using Workbox. You can test this by going offline and then browsing my blog. This note gives a walkthrough of how I did it and summarizes my findings.

TLDR

Steps to enable offline support:

  • Enable service worker
  • Create sw.js with
    importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js');
    workbox.precaching.precacheAndRoute([]);
    
  • Install workbox-cli with npm install workbox-cli --global
  • Follow workbox wizard workbox wizard --injectManifest
  • Inject sw.js with what to cache workbox injectManifest workbox-config.js

What is Workbox?

Workbox is a set of javascript libraries that add support for caching and offline access of web apps. Workbox provides an abstract layer for developers when working with service workers. Some of the things workbox support like precaching, runtime caching, etc will be covered below.

Does your browser support workbox and service workers? Check here

Enable service worker

Add this script to the bottom of your website:

<script>
// Check that service workers are supported
if ('serviceWorker' in navigator) {
  // Use the window load event to keep the page load performant
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js');
  });
}
</script>

This tells the browser to wait until the window load and then register for service worker in /sw.js route.

Using Workbox

There are a few ways to generate service workers using Workbox. I choose to use workbox-cli as I can enable it as part of my build pipeline for my blog which is currently powered by Jekyll. If you use Node or Gulp, you can use workbox-build or if you use Webpack, there is workbox-webpack-plugin.

Install workbox-cli

Install by using npm:

npm install workbox-cli --global

Setup service worker

In sw.js specify the following boilerplate code:

importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js');
workbox.precaching.precacheAndRoute([]) 

Notice we have not specified anything yet to precache. This serves as an injection point for workbox to compute and inject all the routes to be cached.

Use workbox-cli to populate what to precache

Run workbox-cli wizard

workbox wizard --injectManifest

This command provides the option to specify what to cache by looking at all the file extensions in the website. After this is done, a new file called workbox-config.js will be created.

workbox injectManifest workbox-config.js

This command then uses what is specified in workbox-config.js and injects all the files to be cached into the injection point we specified above in sw.js files.

Manually specify what to cache during runtime

There are already a few common recipes that provide examples of caching css, js, images, etc.

I use a few from those recipes for my site:

// Caching Images
workbox.routing.registerRoute(
  /\.(?:png|gif|jpg|jpeg|webp|svg)$/,
  new workbox.strategies.CacheFirst({
    cacheName: 'images',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
      }),
    ],
  })
);

// Cache CSS and JavaScript Files
workbox.routing.registerRoute(
  /\.(?:js|css)$/,
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'static-resources',
  })
);

// Caching Content from Multiple Origins
workbox.routing.registerRoute(
  /.*(?:googleapis|gstatic)\.com/,
  new workbox.strategies.StaleWhileRevalidate(),
);

Add to build and automate

Since I am using CircleCI to deploy changes of my site to Github Pages, I also add the workbox + service worker script generation to the build.

Declared workbox-config.js:

module.exports = {
  "globDirectory": "_site/",
  "globPatterns": [
    "**/*.{html,txt,css,webp,js,json,svg,ico}"
  ],
  "swDest": "_site/sw.js",
  "swSrc": "sw.js"
};

In config.yml file I added:

      # -- more omitted --
      - run: JEKYLL_ENV=production bundle exec jekyll build
      - run: npm install workbox-cli
      - run: npx workbox injectManifest workbox-config.js
      # -- more omitted --

It is quite simple, just added two extra steps to install workbox-cli and run the same command to inject the precache routes.

Considerations

  • Using the workbox-cli, we can cache everything on the site. However, this will not work if the website contains thousands of posts and images as everything will be downloaded to the browser cache.
  • Using the workbox-cli, we can only set service workers to cache our website’s assets. It will not handle caching other website’s sites. This is important because we probably use things like external fonts, javascripts, css files, etc. So we need to manually add that support to our service workers.