Bootstrapping Progressive Web Apps with amp-install-serviceworker

So you’re interested in the super-fast load times that AMP web pages offer, and you’re titillated by the rich, native-like experiences and functionality that Progressive Web Apps (PWAs) can deliver! How do you provide a seamless and instantaneous bridge between the two?

Why, with <amp-install-serviceworker> of course!

In this article we take a look at this AMP tag and build an example of how to bootstrap a PWA service worker via an AMP page.

Why AMP?

AMP pages are super-fast, stripped-down and highly cacheable HTML pages. It’s a technology being pushed by Google and its partners, and it’s gaining a lot of traction. It’s being embraced wholesale by site owners and publishers in droves. Why is it being embraced so much? One reason is because it gives site owners a verifiable way to build fast loading pages. AMP pages often load much, much faster than their non-optimised HTML counterparts.

AMP is about to get a lot more important. Google just announced AMP is coming to general search results, not just the carousel of AMP results it was previously confined to. Some users are already seeing AMP pages mixed in with their search results:

In fact, Google will be highlighting AMP results with a small ⚡ symbol in the search results. Even if your non-AMP page loads as quickly as an AMP page, it won’t have the ⚡ symbol, which, right or wrong, means you’ll be at a disadvantage. The AMP lightning bolt to the side of a search result is a big draw: you know this page is going to load quickly. If you’re not familiar with AMP, you can learn about it here.

Why Progressive Web Apps?

Web technology has evolved to a point where it can offer rich user experiences, with features such as offline capabilities and push messaging that were previously only available to native apps. So, people are justifiably excited now that the web can offer native like functionality without the drawbacks of native apps (such as app store gatekeepers, discoverability, linkability, and maintainability). PWAs represent a coming of age of the web where there are few limits to what can be built with web technologies.

Introduction to amp-install-serviceworker

At Google I/O 2016, <amp-install-serviceworker> got a lot of attention when the Washington Post mentioned that they were using it to bridge the gap between lightning fast load times of AMP pages, and the first load of a cache-enabled PWA. The problem it solves: delivering a seamless, near instantaneous hand-off from an AMP page to a PWA page.

It’s the installation of a PWA service worker via an AMP page that enables this bootstrap, and it’s the amp-install-serviceworker tag that makes this possible. So, let’s see how it works.

First, before we can use <amp-install-serviceworker>, we need to import the amp-install-serviceworker component from the AMP project CDN. Just add this line to the head of your AMP document:

The helpful comment explains the point of introducing a delay before installing the service worker: so that accidental or brief visitors don’t get the service worker installed unnecessarily.

Now to the <amp-install-serviceworker> tag itself:

1

2

3

4

<amp-install-serviceworker src="https://mobiforge.github.io/sw.js"

data-iframe-src="https://mobiforge.github.io/sw.html"

layout="nodisplay">

</amp-install-serviceworker>

The two interesting attributes here are src and data-iframe-src. The src attribute points to the service worker file that is to be installed. Because AMP files can be served from the AMP cache, as well as from the origin server, some mechanism to install the service worker from a separate origin is also needed. This is the what data-iframe-src is for.

When the AMP document is served from the same origin as the service worker, the src attribute is used. When the AMP document is served from the AMP cache, the data-iframe-src document is used to install the service worker. Note that data-iframe-src points to a page that installs the service worker—we’ll come to this later—and the src attribute just points to the JavaScript service worker itself.

The AMP page

Now that we know a little bit about amp-install-serviceworker, let’s take a look at the rest of the AMP page.

First, AMP pages need to start with <!doctype html>, followed by <html ⚡> or <html amp> (ironically, that lightning symbol is slow enough to type on today’s keyboards; copy-paste is your friend here!).

use a canonical link to point the AMP page to the PWA to associate the two

point to a service worker JavaScript (sw.js) file in the src attribute of amp-install-serviceworker

point to a service worker HTML installation page in the data-iframe-src attribute

have added a simple logo (using amp-img), some navigation, and some simple styling

So that’s our AMP page, now let’s look at the PWA and the service worker.

The service worker

Now that we have a method for pointing the AMP page at a service worker, let’s take a look at the other side of the setup: the service worker and the PWA page.

The point of all this is so that we get a lightning fast page load when a user visits the AMP page, and then when the user follows a link to the PWA site, this is also very quick because the service worker was installed and pre-cached some of the PWA resources in the background.

So the service worker needs to download some of the resources needed to get the PWA set up when it’s installed. To demonstrate the concept we’ll create a simple service worker to download and cache the PWA page and its resources. If all has gone according to plan, then we should see that the resource was delivered via cache, despite the user never having visited this page before.

Service worker example code

We’ll make our job a little bit easier by using the sw-toolbox service worker library. This library will help us pre-cache resources, and implement an appropriate caching policy without having to code all the details.

So, first we include sw-toolbox. You can grab it directly from github with git clone https://github.com/GoogleChrome/sw-toolbox.git or via npm with npm install --save sw-toolbox, and deploy it along with your application. We used npm to install the toolbox in our lib folder, so to include it we use:

1

importScripts('/lib/node_modules/sw-toolbox/sw-toolbox.js');

Next we tell the service worker to cache some resources. We use the toolbox.precache function to do this. It expects an array of URLs of resources to cache, and it must be called before the install event is triggered. For this example, we’ll just cache the main PWA page, and the logo. For a proper progressive web app you’d probably precache more resources than we are here; for now we’ll keep it simple. Most PWAs would have a manifest.json file, so we might as well include this too. We also added a bit more app-shell/*chrome to the PWA page, and some javascript to fix the navigation to the top of the web page (we showed how to build the sticky top navigation bar here).

1

toolbox.precache(['pwa.html'],['mf-logo.svg'],['manifest.json']);

This will cache all the things we need to deliver the PWA page. However, although these items have been added to the cache, we still need to set up the service worker to serve them from the cache, to ensure a quick load. The sw-toolbox supports express-style routing, so you can target specific URL and URL fragments to be served with a particular caching policy e.g. cacheFirst, or networkFirst. However to keep it simple for this example, we’ll just set the service worker to serve using cacheFirst policy for everything.

1

toolbox.router.default=toolbox.cacheFirst;

The full service worker code is:

1

2

3

4

5

6

7

8

9

importScripts('/lib/node_modules/sw-toolbox/sw-toolbox.js');

toolbox.precache(['pwa.html','mf-logo.svg','manifest.json']);

toolbox.router.default=toolbox.cacheFirst;

self.addEventListener("install",function(event){

console.log('SW: Installing service worker');

});

(Strictly we don’t need the last three lines, but they’re useful for console debugging!)

The Progressive Web App page

Strictly, for our page to qualify as a PWA, there are a few requirements, such as having a manifest file, having a service worker, and so on (more on these requirements here). We won’t be too worried about ticking all these boxes now; the important thing is to get the service worker bootstrapped.

An excerpt of the PWA page is given below. You can find the rest of the code for this page at github.com/mobiforge. Note that it includes code to install service worker (shown), and extra CSS and JavaScript (not shown) that is not present in the AMP page. This extra CSS and JS represents the richer PWA interface over our AMP page. A bona fide PWA would likely have a richer interface still.

The service worker iframe installer

We also have to provide the sw.html file we referenced in the data-iframe-src attribute. This page can be pretty simple. It just needs to load a service worker in the usual way. Strictly, we could simply load the PWA page itself in the iframe, and not bother with a dedicated service worker installer page, but it makes sense to just include the minimum code needed for installing the service worker at this point, and so we add this page sw.html:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<!doctypehtml>

<html>

<head>

<title>Serviceworkerinstaller</title>

<script type="text/javascript">

varsw="/sw.js";

if("serviceWorker"innavigator){

navigator.serviceWorker.register(sw).then(function(reg){

console.log('ServiceWorker scope: ',reg.scope);

}).catch(function(err){

console.log('ServiceWorker registration failed: ',err);

});

};

</script>

</head>

<body></body>

</html>

So that’s basically it. Now, to test that it has all worked, you should do the following:

1. First visit the AMP page:https://mobiforge.github.io/amp.html
If you add #development=1 to the link (i.e. https://mobiforge.github.io/amp.html#development=1), you can keep an eye on what’s happening via the console log.
All going to plan, it should silently install the service worker in the background, and the service worker will download some resources. You can check the console to see that the service worker has installed and added the items to the cache.

2. Next visit the PWA page:https://mobiforge.github.io/pwa.html
Remember we set the service worker to only serve via cache-first policy. If you want to be really convinced, you could switch your device to offline/airplane mode before visiting this page.

Result: whether you set your device to airplane mode or not, the PWA page should load lightning quick! You can see in the dev tools snapshot below that the network was not used to load the resources.

There you have it, the best of both worlds!

Bonus: You don’t need to let Google control who gets to see the AMP page

You don’t need to wait for Google to crawl your site and notice that you have an AMP page, so that it can then decide whether to surface an AMP link to your visitors. There’s nothing to stop you using device detection to target mobile devices to deliver the AMP page on first visit: if they haven’t visited before, and are on a mobile device, then serve the AMP page. On subsequent visits and page loads then serve the pre-cached, instant-loading PWA site!

This is a website of Afilias Technologies Ltd, a private company limited by shares, incorporated and registered in the Republic of Ireland with registered number 398040 and registered office at 6th Floor, 2 Grand Canal Square, Dublin 2, Ireland