Why developing a PWA using AMP might be right for you

TL;DR : This article talks about developing a PWA using AMP. We will start with a very brief introduction of PWA and AMP and then we will take a deep dive into a step-wise process around developing a PWA using AMP.

What is a PWA

Progressive web app is a web app with added capabilities which makes it Fast, Interactive, Reliable and Engaging (FIRE). Here is a quick comparison of time spent on Native Apps vs Mobile Website - Users spend 6x-7x more time on Mobile native apps as compared with Mobile web.

Source: US Mobile App Report 2017

PWA aims to bridge this gap by creating an App like user experience. Please refer this page for more details about PWAs.

What is AMP

AMP is open source web component library which aims to improve the performance of the web pages. The median load time of an AMP page is less than half a second from Google Search (Source). An AMP page is written using AMP HTML using the inbuilt AMP components . Please refer this page for more details about AMP.

Can AMP work as a PWA?

Definitely, a PWA can be written in AMP and it can use a component called amp-install-serviceworker to install the service worker. The logic in the service worker can offer PWA’s functionalities like adding the app to homescreen and a meaningful offline user experience. This model is described as AMP as PWA on AMP’s official website, in fact https://ampbyexample.com/ itself is a ‘AMP as PWA’.

This is how an ‘AMP as PWA’ can be visualized:

AMP as PWA

Step by step dev guide for developing an ‘AMP as PWA’:

Let’s talk about developing a very simple ‘AMP as PWA’. We will start off with a small AMP code and then we will add ‘Add to homescreen’ and offline UX capabilities.

Step 1: Simply start with a simple AMP page which shows an image on top, followed by an article. The code will go like:

<!doctype html>
<html amp lang="en"><head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <title>Sample AMP as PWA News Website</title>
    <link rel="canonical" href="https://mweb-demos.appspot.com/amp-as-pwa/">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <meta name="description" content="Sample AMP as PWA News Website" />
    <style amp-boilerplate>
        …
    </style>
</head><body>
    <h2>Sample AMP as PWA News Website</h2>
    <amp-img alt="News" src="images/news.png" width="720" height="353" layout="responsive">
    </amp-img>
    <p><i>Image downloaded from <a href="https://pixabay.com/en/coffee-magazine-newspaper-read-791439/">pixabay</a></i></p>
    <div>
        <p>Lorem ipsum dolor sit amet ....</p>
        <p>... Para 2 ...</p>
        <p>... Para 3...</p>
    </div>
</body></html>

This is how it should be looking on an emulator:

Vanilla AMP page in Chrome’s Emulator

Step 2: We need to add the PWA’s capabilities by developing a service worker. We will use workbox for developing a highly efficient service worker. We will need to write a workbox build script like below, which can be used with workbox-cli for generating service workers: (The full code can be found here)

module.exports = {
    "swDest": "sw.js",
    globPatterns: ['images/*.png'], //precache the images
    globDirectory: '.',
    runtimeCaching: [{
        urlPattern: new RegExp('^https:\/\/mweb-demos\.appspot\.com\/amp-as-pwa\/index.html'), //cache the index.html 
        handler: 'networkFirst', // using the network first handler 
        options: {
            cacheName: 'amp-as-pwa-cache'}
    }]
};

All we are doing in the above script is that we are pre-caching the image which comes above the fold and then we are using network first caching strategy for the main article page, so that the users at-least get a stale copy of the page when the device is offline. Please note that we are caching the entire page for simplicity of this exercise, practically you might be rendering the content using REST services. Caching the service’s response and showing the cached copy when the device is offline might be a good idea (assuming that the service returns fairly static content which can be cached). Additionally refer to this offline-cookbook for details around different caching techniques.

Step 3: We will run the build script using command ‘workbox generateSW wb-build-script.js’ to get the serviceworker generated: (please follow the steps mentioned here for setting up workbox-cli)

testuser-macbookpro:amp-as-pwa testuser$ workbox generateSW wb-build-script.js 
Using configuration from /Users/testuser/path-to-build-script/wb-build-script.js.
The service worker was written to sw.js
1 files will be precached, totalling 433 kB.

Generated Serviceworker goes as follows(sw.js):

importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.3.0/workbox-sw.js");self.__precacheManifest = [
  {
    "url": "images/news.png",
    "revision": "ab90df5f7b08e067a5d86f407a2c25ec"
  }
].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});workbox.routing.registerRoute(/^https:\/\/mweb-demos.appspot.com\/amp-as-pwa\/index.html/, workbox.strategies.networkFirst({
    cacheName: "amp-as-pwa-cache",
    plugins: []
}), 'GET');

Step 4: Create the PWA manifest (manifest.json) which is required to provide ‘Add to Homescreen’ functionality:

{
    "short_name": "AMP as PWA",
    "name": "Sample AMP as PWA News Website",
    "start_url": "index.html",
    "icons": [
        {
            "src": "icons/icon-192.png",
            "type": "image/png",
            "sizes": "192x192"
        },
        {
            "src": "icons/icon-512.png",
            "type": "image/png",
            "sizes": "512x512"
        }
    ],
    "background_color": "#D6DBDF",
    "theme_color": "#D6DBDF",
    "orientation": "portrait",
    "display": "standalone"
}

Step 5: Create a HTML file (sw.html) which will have the JS code required for ‘Add to homescreen

<!doctype html>
<html>
<head>
<title>installing service worker</title>
<script type="text/javascript">//Register the service worker 
        if("serviceWorker" in navigator) {
            navigator.serviceWorker.register("https://mweb-demos.appspot.com/amp-as-pwa/sw.js").then(function(reg){
                    console.log('ServiceWorker register with scope: ', reg.scope);
            }).catch(function(err) {
                    console.log('ServiceWorker registration failed: ', err);
            });
        }else{
            console.log('SW is not supported');
        }
        
//intercept beforeinstallprompt event and show the Add to Homescreen prompt 
        window.addEventListener('beforeinstallprompt', (event) => {
              console.log('beforeinstallprompt event fired')  
              event.preventDefault();
              event.prompt();
              
              event.userChoice.then((choice) => {
                    if (choice.outcome === 'accepted') {
                      console.log('User accepted the A2HS prompt');
                    } else {
                      console.log('User dismissed the A2HS prompt');
                    }
              });
              
            });</script>
</head>
<body>
</body>
</html>

Step 6: we will include the manifest’s link in the AMP HTML. Simply put the following in the HEAD section

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

Step 7: This is the last step, we will use an AMP component called amp-install-serviceworker to add the server worker to the website

<!-- Putting amp-install-serviceworker below the fold -->
<amp-install-serviceworker src="https://mweb-demos.appspot.com/amp-as-pwa/sw.js"
data-iframe-src="https://mweb-demos.appspot.com/amp-as-pwa/sw.html" layout="nodisplay">
</amp-install-serviceworker>

This code should ideally be put after the above-the-fold. The completed index.html is published here. Now we can deploy and verify if the service worker is running and the files are cached (Using Application tab in chrome’s dev tool):

Navigate to Applications tab to inspect if the service worker is running:

Service Worker is activated and is running

Inspect the ‘Cache Storage’ to make sure that the required assets are cached:

amp-as-pwa cache is created

This is how the completed ‘AMP as PWA’ page looks after publishing it:

Completed PWA in action

The completed code is published at https://mweb-demos.appspot.com/amp-as-pwa/index.html . You can scan this QR code to launch it on mobile

Benefits of writing PWA using AMP

  • One code can work on Desktop and mobile. So essentially we can live with just one version of code instead of one codebase for desktop, one AMP canonical codebase and one PWA codebase. So that’s a potential 3X saving in the amount of development effort required.
  • It’s written in AMP so you’re assured of a certain level of performance out of the box. This is especially important because, though AMP itself is not a ranking factor, speed is.

Potential Caveats

AMP doesn’t allow developer or third party JS code to be used, but it provides a vast array of inbuilt components which obviates the need of external JS to a major extent.

Dev Channel

Developers Channel - the thoughts, opinions and musings from members of the Chrome team.

Prashant Mishra

Written by

Mobile Technical Solutions Consultant @ Google

Dev Channel

Developers Channel - the thoughts, opinions and musings from members of the Chrome team.