Progressive Web Apps

Show Up for Offline First. Stay for the Killer Performance Boost.

by Jason Lengstorf
@jlengstorf |


How much does your websitecost?

We measure cost in seconds:

seconds to load popular websites

Source: Time To Interactive from Lighthouse. I ran the test twice and took the faster time.

Time isn’t the only cost

Mobile Data Can Be Expensive


Price in USD for a US-based AT&T customer traveling internationally.

Websites are getting larger

website size from 2012 to 2017

Source: HTTP Archive

Would you pay $10
to load this website?

cost in USD to load popular websites

Also: loading huge sites on mobile just sucks

Chrome’s no connection screen

...and what if the connection is flaky?

We Can Fix It!

(And it doesn’t mean starting from scratch.)

The Solution:

Progressive Web Apps

With Progressive Web Apps (PWAs), we can:

  1. Significantly decrease the amount of data transferred on each page load
  2. Improve the experience for users on unreliable connections


“That’s not my target market.”

eat your veggies

Can’t we just ignore PWAs?


Here’s why:

  • PWAs decrease load times on all connections
  • Perceived load times improve
  • Stability improves for mobile users everywhere
  • User experience is better all around

Real-World Examples
  • Cold: ~3s (477 KB)
  • Warm: ~1s (0.5 KB)
  • Cold: ~1.5s (339 KB)
  • Warm: ~1s (1.1 KB)

That sounds significantly more awesome than eating vegetables.

What makes a PWA?

  • Web-based application
  • Provides a Web Manifest
  • Registers a Service Worker

Web Manifest

  "short_name": "AirHorner",
  "name": "Kinlan's AirHorner of Infamy",
  "icons": [{
    "src": "launcher-icon-1x.png",
    "type": "image/png",
    "sizes": "48x48"
  "start_url": "index.html?launcher=true"

Service Worker

self.addEventListener('install', function(event) {
  event.waitUntil('mysite-v1').then(function(cache) {
        // add non-critical assets
      return cache.addAll(
        // add critical assets

What Service Workers do:

  • Cache critical assets when first installed
  • Precache non-critical assets in the background
  • Act as a proxy for all outgoing requests
  • Skip server & provide cached assets if possible

This allows Progressive Web Apps to
load offline on subsequent visits

But I have some bad news

Service Workers alone aren’t enough to make our apps blazing fast

(They don’t help at all on the first visit.)

To make JS-powered apps really fast, we should:

  • Implement route-based code splitting
  • Use HTTP/2 to serve assets
  • Set up background prefetching
  • Lazy load any non-critical assets

You may have heard this called the PRPL Pattern

Addy Osmani

Addy Osmani





To make JS-powered apps really fast, we should:

  • Implement route-based code splitting
  • Use HTTP/2 to serve assets
  • Set up background prefetching
  • Lazy load any non-critical assets
  • Eliminate all unused code
  • Use server-side rendering to generate static files

That’s a lot of things.

Kanye WTF.

Route-based code splitting:

  • Creates smaller bundles for specific routes
  • Creates shared bundles for common code
  • Allows loading only what’s really needed
App build bundles.

Source: Google Developers

Code splitting with Webpack:

  • Use import() to identify chunks
  • in Webpack 4, use optimization.splitChunks
  • In Webpack 3 or earlier, use CommonsChunkPlugin

Totally True Internet Fact

Summoning Sean Larkin

If you say “Webpack” 3 times, Sean Larkin will appear.

Serving assets with HTTP/2:

  • Allows multiplexed downloads
  • Provides push capabilities to minimize requests

Asset prefetching:

  • Assumes which routes the user will load next
  • Loads the assets for those routes in the background
  • Makes loading the next route almost instantaneous

Lazy loading assets:

  • Prevents images from blocking the initial load
  • Only requests them once they’re in the viewport

“Won’t lazy loading make the page jump?”

Page jumping while images load.


We can fix it!

Lazy loading with loading animation

Lazy loading with animation.

Source: responsive-lazyload.js

Lazy loading with the blur-up technique

Lazy loading with animation.


Lazy loading with traced SVG

Lazy loading with traced SVG.

Source: Gatsby.js

Lazy loading with simplified polygons

Lazy loading with SVG polygons.

Source: Gatsby.js

Eliminating unused code:

  • Reduces the overall size of the codebase
  • Helps avoid module bloat
Heaviest objects in the universe.

Source: Reddit?

How to remove unused code:

  • Use Uglify.js to remove dead code
  • Enable tree-shaking with ES modules
  • Analyze your bundle output
Webpack Bundle Analyzer.

Source: Webpack Bundle Analyzer

Serving only static assets:

  • Removes the need for runtime server rendering
  • Eliminates an extra HTTP round-trip
  • Improves cacheability

😱 Server-side rendering?! 😱

But it’s hard!


Loading 1MB of blocking JavaScript before anything can be displayed


Showing a loading spinner while the framework initializes


Server-side rendering so no JavaScript is required to initially display the page

“But static files won’t work for my app!”

Yes they will. Probably.

If all of this seems a little overwhelming I totally understand

Performance is hard

We can get most of these performance improvements for free

Open source is awesome. 🎉💕

How to improve web performance for free:

  • sw-precache and/or sw-toolbox
  • Next.js + Now
  • Gatsby.js + Netlify

sw-precache / sw-toolbox

  • Helper libs from the Google team
  • Sets up sane defaults for Service Workers
  • Requires manual configuration


  • Framework for server-side rendering React
  • Automatic code-splitting
  • Requires a server


  • Hosting for Node, static, and Docker applications
  • Purpose-built for Next.js apps
  • Amazingly simple CLI tooling


  • Framework for building React apps
  • Compiles to static files
  • Add Service Worker + Web Manifest with plugins
  • Generated apps are unbelievably fast


  • Deployment and hosting for static sites
  • Powerful Git workflow integrations
  • Automatic, free SSL generation

Let’s Recap

PWAs are the future


Blazing fast apps need fine tuning️

PWA Performance Checklist:

✅ Add a Service Worker
✅ Create a Web Manifest
✅ Follow the PRPL Pattern
✅ Remove unused code
✅ Generate static files

Use frameworks to automate perf best practices

  • sw-precache and sw-toolbox
  • Next.js + Now
  • Gatsby.js + Netlify

Spending time on performance is good for everyone.

  • Lower cost to pay-per-MB users
  • Better experience on mobile and low-powered devices
  • Improved reliability on spotty connections
  • Apps feel more “solid” on all devices
  • Repeat visitors see damn-near instant page loads

When we focus on performance everybody wins


Jason Lengstorf

Jason Lengstorf
Follow me on Twitter: @jlengstorf


  1. Jake Archibald’s Offline Cookbook
  2. Lighthouse
  3. sw-precache
  4. sw-toolbox
  5. responsive-lazyload.js
  6. Webpack
  7. Gatsby
  8. Netlify
  9. Next.js
  10. Now
  11. PRPL Pattern

Tweet: @jlengstorf #PerfMatters