Making a PWA with Netlify

I've been using a service called Netlify recently, which is based entirely around the idea of static hosting. I decided to try and convert the popular TodoMVC application into a more complete progressive web app, and that's where we start our journey. I also published this article in video form, available here.

What's a progressive web app?

The concept behind being a progressive web app is to create a webpage providing a high enough level of quality to earn a place on a users home screen. There's a great page explaining more on the Google developers site, here.

Sounds great! Where do I start?

It's important to understand that progressive web apps are a concept, rather than a discrete set of rules. Each browser has its own set of criteria, and so you should research these before going forward. Lighthouse is a great starting point. It's a browser extension which generates a report for each page of your site identifying areas for improvement. For the sake of this article, we'll rely on it as our way of deciding what to focus on.

I ran the tool for our basic todo list app, and it highlighted a few things which we were missing:

  • HTTPS - our page isn't secure, and with services such as Let's Encrypt offering SSL for free, there's really no excuse not to encrypt your content. I was able to fix this quickly using Netlify's built in HTTPS integration, so I won't cover it too much in this article.
  • Critical Request Path - This is an indicator of the number of requests your browser has to make before it is able to reach what we call the 'first meaningful paint'. This refers to the point at which users can read the content on your site.
  • Application Manifest - Manifest files allow extra context to be given to the browser so that it can provide a native like experience when the app is added to a users home screen.
  • Service Workers - The ability to add a middleman between your browser and the internet, to provide a better experience with either unpredictable network conditions, or even in situations where the user is offline.

The easy bit - HTTPS, and optimising assets

2017 is a great year to be a web developer. HTTPS used to be so expensive it was reserved for large organisations and financial institutions, but it's now so cheap browsers such as Chrome (see here) are beginning to warn users against any site lacking this protection. Services such as Cloudflare and Netlify will add this for you, but if you're running your own server, tools such as Caddy are available which will setup TLS hassle free.


To optimise assets, I turned to the built in asset bundler in the Netlify panel (available to Pro users, and free for open source projects). If you aren't hosting a static site, you can achieve something similar with tools like Aspax for Node or Gulp for anything else.

Adding an Application Manifest

{
  "name": "TodoMVC",
  "short_name": "TodoMVC",
  "start_url": ".",
  "display": "standalone",
  ...
  View Source

Adding an application manifest really doesn't take too long. If you've worked with JSON before, it's quite simply a case of running through the keys listed here and setting them to suit your app. You'll also need the following meta tag in your header:

<link rel="manifest" href="/manifest.json">
I was initially confused as to which extension I should use, but Addy from Google helped me out over on Twitter:

What's the point of all this? Browsers will notice that you're offering everything a user would need if the app was to be placed onto their home screen, and so it'll ask them if they're interested. If they say yes, your app will appear with a native-like splash screen and in a fullscreen window without any of the browsers usual chrome. If you'd like to know what this looks like in terms of UX, an animation and further explanation is posted here.

Service Workers

New to the web, but being adopted quickly, Service Workers allow you to take control of the network and speed up page load times by deciding yourself what should be cached. When used wisely, they can be of great benefit especially in letting users access your web app even when offline. They can also be used to provide push notifications and more, but that's beyond the scope of this article.

For TodoMVC, I decided the app wasn't likely to change. As a result, I implemented a basic service worker which simply permanently caches requests and serves them up when they are requested again.

if ('serviceWorker' in navigator) {
  //Let's register the ServiceWorker
  navigator.serviceWorker.register('/sw.js').then(function () {
    //We're good to go
    console.log("ServiceWorker registered correctly.");
  }).catch(function (err) {
    console.log("ServiceWorker registration failed: " + err);
  });
}
View Source

Registering the service worker is just a case of checking to see if our browser supports the feature, and if so, calling the register function. This then triggers an install event inside this file, at which point we call self.skipWaiting() to activate the Service Worker immediately rather than after all related tabs have been closed. The code for this is a bit longer, so take a look at GitHub if you're interested. MDN has a great article taking you through the process step by step if Service Workers are new to you.

Conclusion

After making the changes listed above, I've gone from scoring 25/100 on Lighthouse to a full 100%. It only took a few days, and the existing codebase remained almost identical, showing that any app can be turned into a progressive web app with some thought.

If you'd like to take a look for yourself:

  • Here's the original implementation of TodoMVC, which is available on GitHub too. This can be considered my starting point.
  • The final site is here, proudly hosted by Netlify. Netlify makes deploying static sites easy and their website is full of handy documentation to get started.