Introduction to Progressive Web Apps: Service Worker and Web App Manifest

Thanks to technologies like Service Workers, websites and web apps can now behave more and more like native apps on browsers that support it. That's what we then call a progressive web app (PWA).

Progressive web apps allow you to do things like install to home screen and make your app accessible when offline. Try it out, if you are on Chrome or Firefox, turn off your Wi-Fi and reload this page. You’ll see that the page loads fine because it’s been cached and the cached version is served from the Service Worker.

In order for a web app to be considered a progressive web app, a few things need to be in place:

Progressive Web App Checklist

The following 3 are the most important to have a basic progressive web app working:

  • The website has to be served over https
  • Your app should have a Web App Manifest file
  • Your app should have a working Service Worker

Having these three things covered you’ll have a basic Progressive Web App that can be available offline and loads static assets from the cache. You can use the Lighthouse Chrome extension to run an audit and see where your app is standing in the spectrum:

Lighthouse in action

Web App Manifest

A web app manifest is a Json file with metadata about your app. The content from the manifest is especially important to allow browsers like Chrome on Android phones to present an option to repeat visitors to install the web app on their home screen.

This Web App Manifest Generator makes it easy to generate a manifest.json file. It’ll even generate all the different icon sizes from just one 512x512 icon.

Once you have your manifest.json file, include it in the head section of your app with something like this:

<link rel="manifest" href="/path/to/manifest.json">

Here’s for example the content of the Web App Manifest for this website:

  "name": "",
  "short_name": "Alligator",
  "theme_color": "#138e69",
  "background_color": "#f8ea48",
  "display": "browser",
  "Scope": "/",
  "start_url": "/",
  "icons": [
      "src": "images/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
      "src": "images/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
      "src": "images/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
      "src": "images/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
      "src": "images/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
      "src": "images/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
      "src": "images/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
      "src": "images/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
  "splash_pages": null

Service Worker

Service Workers is a new technology available in some modern browsers that allow control over network requests. Service Workers make it possible to cache assets and control what happens when there’s no access to the network or when the access is very slow. In the case of web apps, assets for the shell of the app can be cached and then always available when offline or in low connectivity.

Registering a Service Worker

The first step in getting a service worker in place is to register it.

Here’s a sample script that you can use to register a Service Worker. Notice how it first checks if Service Workers are supported:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
    console.log('ServiceWorker registration successful!');
  }).catch(function(err) {
    console.log('ServiceWorker registration failed: ', err);

Notice how the Service Worker is in the root folder. This means that it’ll be scroped to the whole web app. If it had been in a subfolder, it would be scoped to just that subfolder by default.

The Service Worker

Now for the service worker itself, it can get quite complex, and a tool like SW-Toolbox can really help cut the boiler plate code needed to setup a Service Worker. Manually coding a Service Worker involves setting up event listeners for the install, activate and fetch events, but SW-Toolbox takes care of that behind the scenes.

You can install SW-Toolbox from npm like this:

$ npm install --save sw-toolbox

Then, in the Service Worker that you registered (e.g.: service-worker.js), use self.importScripts() to import SW-Toolbox:


For this website, all that was needed other than importing SW-Toolbox is the following:

self.toolbox.precache(['/images/logo-small.svg', '/images/web-icons/webicon-twitter.svg', '/images/web-icons/webicon-facebook.svg']);
self.toolbox.router.get('/*', toolbox.networkFirst);

This precaches a few assets and then caches everything, but tries to get fresh data from the network first before defaulting to the cached version if the network is not available.

This works well for a static website like this one, but caching everything wouldn’t work with an actual web app that has dynamic data. You can dig peeper into what you can do with SW-Toolbox with this usage guide and this blog post.

If you want to dig peeper into Service Workers, Mozilla put together a Service Worker Cookbook with sample code for various things service workers can do.


Once you have everything in place, you can inspect the Web App Manifest and Service Worker from the Application tab of the Chrome DevTools:

Inspecting Service Worker

You can play around with the Offline checkbox to test how your app would react when offline. When playing around with different Service Worker configurations, it’s also useful to be able to unregister the current service worker so that the new version of the service worker gets registered instead.

Learning More

As this was just a brief introduction, you may want to dive deeper into progressive web apps. Here are a few good resources:


🔎 Searching...