A related codelab explains how to integrate service worker into an existing application and how to make it work offline. In this codelab, we'll retrace those steps but this time we'll use a tool called sw-precache to add offline functionality with only six lines of code. It's never been easier to add service worker support to an existing app, and we'll show you how in this codelab.

What you'll learn

What you'll need

How will you use this tutorial?

Read it through only Read it and complete the exercises

How would rate your experience with building Web apps?

Novice Intermediate Proficient

The sample app we're going to use in this codelab is the venerable "Airhorner", which simulates, you guessed it, an airhorn. You can either download all the sample code to your computer in an archive file...

Download Zip

...or clone the GitHub repository from the command line.

$ git clone https://github.com/GoogleChrome/airhorn.git

Next, download and install Node.js from https://nodejs.org/. Node.js includes npm, the node package manager. Over the course of the codelab, we'll use npm to install our build tool, gulp, as well as the sw-precache node module, which we'll use to automatically generate our service worker code.

With the code downloaded, the following instructions describe how to build and start testing the Airhorner app.

Check out the code-lab branch to get the starting version of the app.

$ git checkout code-lab

You can now run the site using either your favorite HTTP server or by using Python. This command sequence starts a web server running on localhost port 3000:

$ cd app
$ python -m SimpleHTTPServer 3000

Open the site in Chrome and you should see a page like this:

Make sure your speakers are on, click the horn and it should make a fairly noticeable sound.

Now kill the server (ctrl-c in the command line). This simulates the network going offline. Then reload the site in the browser. It should show you an error message somewhat like this one:

This fails because our app doesn't yet have offline support, and that's exactly what we'll add in this codelab. But rather than the service worker code by hand, we're going to use the sw-precache module to generate it and make our lives easier.

As you may recall from the Offline codelab, in order to add offline functionality to a web app, you need to complete the following steps:

  1. Create a JavaScript source file that imports the service worker polyfill and defines your service worker event handlers. This file can have any name, but we'll call it sw.js in this example.
  2. Add markup to your index.html file to register your service worker.

The tool we're going to use in this codelab, sw-precache, is a Node.js module that automatically handles step 1 for you. It magically builds the right event handlers for you so you don't need to write any of the code that goes in sw.js. But we'll take a look at the generated code and make sure we understand what it's doing.

This project uses gulp as its build management tool. The package.json file defines our Node.js package requirements. Install all of the package dependencies listed in package.json by running the following command:

$ cd .. # go back to top-level directory
$ npm install

Now install the sw-precache module by running the following npm command (depending on how Node.js and npm are installed, you may or may not need to use the sudo prefix):

$ npm install --save-dev sw-precache

A quirk of gulp is that you typically need to have a global instance of it installed as well as the local instance, so run one more command to complete the setup:

$ npm install -g gulp

Now we can use the gulp command to build our project but first we need to create a gulp task that uses the sw-precache module to generate our sw.js file. Add the following code at the bottom of your gulpfile.js:

gulp.task('generate-service-worker', function(callback) {
  var path = require('path');
  var swPrecache = require('sw-precache');
  var rootDir = 'app';

  swPrecache.write(path.join(rootDir, 'sw.js'), {
    staticFileGlobs: [rootDir + '/**/*.{js,html,css,png,jpg,gif}'],
    stripPrefix: rootDir
  }, callback);
});

For more information about the sw-precache API, check the project page on Github. This code tells the swPrecache module to write a file at sw.js, which arranges to cache all objects in this app ending with extensions js, html, css, png, jpg, and gif.

Now run gulp generate-service-worker. You should see output like the following:

$ gulp generate-service-worker
[11:56:22] Using gulpfile ~/airhorn/gulpfile.js
[11:56:22] Starting 'generate-service-worker'...
Total precache size is about 75.87 kB for 11 resources.
[11:56:22] Finished 'generate-service-worker' after 49 ms
$

This process generates a new sw.js file in the app directory of your project.

Because sw-precache is a build-time code generation tool, it has direct access to all your local resources, and can efficiently calculate the hash of each to keep track of when things change. It uses those changes to trigger the appropriate service worker lifecycle events and re-downloads only modified resources, meaning that updates are small and efficient, without requiring the developer to manage versioning.

The service worker code generated by sw-precache will cache and serve the resources that you configure as part of your build process. For mostly static sites, you can have it precache every image, HTML, JavaScript, and CSS file that makes up your site. Everything will both work offline, and load fast on subsequent visits without any extra effort. For sites with lots of dynamic content, or many large images that aren't always needed, precaching a subset of your site often makes the most sense.

You can combine sw-precache with one of the service worker "recipes" or techniques outlined in the offline cookbook to provide a robust offline experience with sensible fallbacks—e.g. when a large, uncached image is requested offline, serve up a smaller, cached placeholder image instead. You can also use wildcards to precache all of the resources that match a given pattern—there's no list of files or URLs that you have to manually maintain.

Add the following <script> tag to the bottom of your index.html file to register your service worker:

<script>
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').then(function() { 
    console.log("Service Worker Registered"); 
  });
}
</script>

The above snippet checks to see if the browser supports service workers, and if it does, calls the register method, which returns a Promise. After the registration is completed, the browser will resolve the Promise and calls the function in the .then() clause. (Note: this happens asynchronously.)

This is a very simple service worker registration snippet. From the main page, in addition to registering your service worker, you also have the opportunity to listen for service worker lifecycle events. For example, you could display a message to your users saying "Hey, something's been updated in the background, please refresh this page."

You can see a more complete, sophisticated implementation of this sort of service worker lifecycle management code here.

You now have everything you need to convert your airhorn app into an offline airhorn app. Let's now test that claim by running the site using either your favorite HTTP server or by using Python. This command sequence starts a web server running on localhost port 3000:

$ cd app
$ python -m SimpleHTTPServer 3000

Open the site in Chrome and you should see a page like this:

Turn up your speakers, click the horn and it should make the now familiar airhorn sound.

Open chrome://serviceworker-internals/ in Chrome. This will show you a list of all the registered service workers, which you can use to verify your service worker has indeed properly registered.

Now kill the server (ctrl-c in the command line). This simulates the network going offline. Then reload the site in the browser and you should see the same page you saw when the server was running. The airhorn should also work, just as it did before.

Give a little honk of the air horn. You've just offlined your airhorner app with only six lines of code!

What we've covered

Next Steps

Learn More