Progressive Web Apps

Dan Shea



This talk was inspired by numerous talks at Full Stack Conference, by Skills Matter.


The aim here is to amalgamate those talks into a more succinct presentation aimed at a smaller audience.


I am by no means an expert on this area. I learned some things that were cool, and thought I should share them.


  • Act One: Why should I care?
    • ​Somebody please think of the users
  • Act Two: Cool story bro, what's available?
    • Live browser features
  • Act Three: Show me!
    • Production Demos
    • Live coding + samples
  • Act Four: Show me moar!
    • In dev browser features

Act One

Why Should I Care?

Story Time

You're on the train...

Inputting fields into a web form

You hit send on this important piece of work...

A wild tunnel appears!

You wouldn't have this issue on a native app

You shouldn't have this issue in a web app either!


As we'll see service worker makes it easier to deliver

  • Offline content

  • Background Sync

+ More


Native Apps & Mobile Web


Sweeping statistics & generalisations

People use mobile apps more than web apps

Native Apps & Mobile Web

Not all users want an app:

Bottom Line

Further considerations

  • Web apps need a connection
    • Even for static content
  • UK/MEDCs
    • Lie-Fi, slow torturous
    • Caps on internet usage
  • Emerging Markets
    • Less access to internet
    • Slower internet speeds
    • Higher costs per Mb of usage

Act 2 

Cool story bro, what's available?

Progressive Web Apps!!!

Recent browser features that enable web applications to have a user experience more akin to a native mobile application.

Live Features

  • App Shell
  • Cache Control
  • Home Screen Save
  • Push Notifications
  • Background Sync
  • Powered By...


App Shell

  • Minimal HTML, JS, CSS
  • Usually no data
  • Quick load, like an app



  • Does not suit all sites
  • Don't have to be super strict
  • However if you can it's nicer UX

Service Worker Basics

  • Register
  • Install
  • Activate
  • Fetch
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js', {
        scope: '/'
    }).then(function (registration) {     
        console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }).catch(function (err) {
        console.log('ServiceWorker registration failed: ', err);

Initialising the Service worker

Register event (site)

Install Event

var cacheName = 'danCodeMonkeyV' + version,
    appShellFiles = [
    ignoredUrls = [

self.addEventListener('install', function (e) {

	.then(function (cache) {
            // Atomic, one fails, it all fails
        // Older service workers will cause this one to "wait". 
        // This skips the waiting stage.

(App shell initialisation) - (sw)

self.addEventListener('activate', function (e) {

    e.waitUntil(caches.keys().then(function (keyList) {

        // Flushing the old cache here
        return Promise.all( (key) {

            if (key !== cacheName) {
                return caches.delete(key);
    .then(function () {
        self.clients.claim(); // Force claim all tabs

Activate Event (sw)

  • Remove old caches
  • Force new service worker to claim tabs.

Cache Control / Offline First

self.addEventListener('fetch', function (event) {
        caches.match(fetchRequest).then(function (response) {

            if (!navigator.onLine && response) {
		return response;
	    } else {
		return caches.match('/offline');

            return fetch(fetchRequest).then(function (response) {
                return (cache) {
                    cache.put(fetchRequest.url, response.clone());
		    return response;

Fetch Event

  • Basic offline only cache
  • Simplified for demo

Caching Strategies

Live Examples!!!!!

Tips & Tricks

  • chrome://serviceworker-internals"
  • Close ALL related windows to remove unreg SW
  • Clear cache as well
  • If no skip, reload page to active new SW
  • Use porno tab when debugging SW - clean slate
  • For production use a library to help with edge cases
  • Scope, sw.js needs to be at root, if you want root scope.

Single Page Applications


Home Screen


  "name": "{{site.title}}",
  "icons": [
      "src": "{{site.baseurl}}{{site.icon-36p}}",
      "sizes": "36x36",
      "type": "image/png"
      "src": "{{site.baseurl}}{{site.icon-48p}}",
      "sizes": "48x48",
      "type": "image/png"
      "src": "{{site.baseurl}}{{site.icon-72p}}",
      "sizes": "72x72",
      "type": "image/png"
      "src": "{{site.baseurl}}{{site.icon-96p}}",
      "sizes": "96x96",
      "type": "image/png"
      "src": "{{site.baseurl}}{{site.icon-144p}}",
      "sizes": "144x144",
      "type": "image/png"
      "src": "{{site.baseurl}}{{site.icon-192p}}",
      "sizes": "192x192",
      "type": "image/png"
  "start_url": "{{site.url}}",
  "display": "standalone",
  "background_color": "#000",
  "theme_color": "#00cdff"
  <!-- iOS Web App mode -->

  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="Dan Shea">
  <link rel="apple-touch-icon" sizes="36x36" href="/img/web-app/icon-36p.png">
  <link rel="apple-touch-icon" sizes="48x48" href="/img/web-app/icon-48p.png">
  <link rel="apple-touch-icon" sizes="72x72" href="/img/web-app/icon-72p.png">
  <link rel="apple-touch-icon" sizes="96x96" href="/img/web-app/icon-96p.png">
  <link rel="apple-touch-icon" sizes="144x144" href="/img/web-app/icon-144p.png">
  <link rel="apple-touch-icon" sizes="192x192" href="/img/web-app/icon-192p.png">

  <!-- Android Web App mode -->

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

Add To Head

Web Install Banners

Web Install Banners

Chrome won't let you decide when to display... 

Can defer or cancel the prompt

Alternatively, click/tap the menu, click save to desktop/homescreen

Push Notifications

When To Use

Google Cloud Push Notifications

  • Create Project
  • Create Credentials
  • Add Credit card details
    • Apparently for "Not a robot"...
  • Add ProjId to Manifest
    • Google docs out of date
    • Stack overflow out of date...
  • Verify domain

Add the Codez

  • Reg
  • Push manager
  • SubEndpoint,
    • save on server prod
    • captured in debug
function subscribe() {
    reg.pushManager.subscribe({ userVisibleOnly: true }) // userVisibleOnly = always show
       .then(function (pushSubscription) {
            sub = pushSubscription;
self.addEventListener('push', function (e) {
        self.registration.showNotification('New Blog Post', {
            'body': 'A new blog post is up, check it out!',
            'icon': 'img/icons/icon144.png'

Push manager - subscribe - (site)

Push event (sw)

Background Sync


  • Register Sync Event
  • Consume Sync Event
  • Periodic Sync
  • Index DB
  • Futher reading:


Was working last time I gave the talk > 1 year a go.

Broken on my site.

Broken on the google demo site.


Recent Advancements

Act Four

In dev features!!!

Web Bluetooth

Cute, but is it ready?

Why not just use AppCache?


  • App like
    • Performance, relability, UX
  • Save to home screen
  • Push Notifications
  • App Shell
  • Offline / Caching
  • Background Sync

Work Shop

Futher Learning!!!

Progressive Web Apps

By Dan Shea

Progressive Web Apps

  • 1,825
Loading comments...

More from Dan Shea