Progressive Web Apps

What has the web ever done for us?

Gareth Jones - @nomiddlename

The Plan

  1. Native apps are awesome!
  2. Native apps suck!
  3. What can we do? (SPOILER: PWAs)
  4. How do we do it?
  5. Browsers: Oh, FFS (SPOILER: Google)
  6. The summary for the people who fell asleep

Native apps are awesome!


Time to first interaction

The average load time for mobile sites is 19 seconds over 3G.
53% of mobile site visits are abandoned if pages take longer than 3 seconds to load.
Doubleclick Report

Repeat customers

Home screen

Push notifications

Up yours, WWW.

Native apps suck!

Most people don't install apps

People aren't looking for apps

App stores


Every step between your users and installing your app loses you 20% of them.

Gabor Cselle

Things to do in Canberra

Go on, go on, go on.

What has the web ever done for us?


Well, there's links

...and searching

...and easy distribution

But apart from that?


In summary

  1. Nobody will find your app.
  2. Nobody will install your app.
  3. Nobody will use your app.
  4. You will die penniless and alone.

If only there was another way?

Surprise: Progressive Web Apps!


Web Apps


Time to first interaction

Service Workers!

Getting onto the home screen


SpammingIncreasing Customer Engagement

Push notifications! - demo site

Service Workers

image/svg+xml Browser Browser The Internet The Internet Service Worker Service Worker Cache Cache

1. HTTPS only (localhost counts as secure)

2. Let the browser know about the service worker.

// this is in your main javascript
if ('serviceWorker' in navigator) {
    .then(() => {
      console.log("Service worker registered!");

3. The browser installs the service worker

//in your service worker - sw.js
self.addEventListener('install', function(e) {
  e.waitUntil( => {
      return cache.addAll(filesToCache);

var cacheName = 'pow-v30';
var filesToCache = [
  //other css, etc.

4. The browser triggers an activate event

//in your service-worker - sw.js
self.addEventListener('activate', function(e) {
    caches.keys().then(keys => {
      // delete any old caches

5. Now we can intercept requests

//in your service worker - sw.js
self.addEventListener('fetch', function(e) {
  e.respondWith( => {
      return cache.match(e.request).then(response => {
        return response || fetch(e.request);

6. Serve our app skeleton from the cache

Don't use my dodgy code.

sw-precache (

sw-toolbox (


Makes your app installable.


  "short_name": "POW! 2016",
  "start_url": "index.html",
  "display": "standalone",
  "icons": [{
      "src": "images/logo.png",
      "sizes": "192x192",
      "type": "image/png"

When do you get to install?

  • HTTPS + Service Worker
  • Manifest with "short_name", "start_url", and an icon
  • User visits at least twice, with at least 5 mins between


beforeinstallprompt event

Push notifications

1. Ask for permission

image/svg+xml Browser Browser User User Browser Browser Push Service Push Service Server Server

//in your main javascript
navigator.serviceWorker.ready.then(reg => {
  Notification.requestPermission().then(() => {
      userVisibleOnly: true
    }).then(sub => {
      // PushSubscription

2. Send a message

image/svg+xml Server Server Push Service Push Service Browser Browser Service Worker Service Worker User User

//server-side code
const push = require('web-push');
  { userPublicKey: '...',
    userAuth: '...',
    payload: '...' }
).then(() => {
  console.log('Message sent.');

3. Receive the message

//meanwhile, back in our service worker
self.addEventListener('push', function(e) {
      "Schedule change",
      { body: e.body.json().message, icon: '...' }


Up yours, native apps!

Browsers: Oh, FFS

Browser support

Android - two thumbs up

  • Service-workers ✓
  • Manifest ✓
  • Push notifications ✓


  • Service-workers ✗
  • Manifest ✗
  • Push notifications ✗


PWAs bring some benefits of native apps

  • Time to first interaction - Service Workers!
  • Install to home screen - Manifests!
  • User engagement - Push Notifications!

But they don't work on iOS (yet)


Who was that masked man?

Gareth Jones