Enable Offline Support to Websites with Workbox

Published: | Last Edited:
Category: tech

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:

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/6.5.3/workbox-sw.js');
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

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.ExpirationPlugin({
        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

Footnotes:

Next Post: I Turned My Blog into a Progressive Web App
Previous Post: Three Proofs Total Stock Market is Efficient