Run an online business long enough and it’s impossible not to come across Progressive Web Apps. Once you have, you might be naturally drawn to the many benefits a PWA offers, but how do you know it’s the best choice for your application? One way is to start with Service Workers: the unsung heroes of PWAs’ success and merciless Lie-Fi busters. Understanding their impact on performance, accessibility and engagement might be the key to unlocking new business value, without losing sight of potential challenges. This quick walkthrough focuses on the essential components, definitions and strategies to help you keep calm and make Service Workers work—both on and offline.

Service Workers in PWAs

Working with Service Workers: why and how?

The Service Worker file is probably the single most important piece of JavaScript code in every PWA. Acting as an in-browser proxy, it handles requests sent from the web app and returns responses from the cache or the network. This busy middleman plays the main role in optimising performance on different levels, most notably:

  • reduces load times and data usage
  • makes the app installable
  • enables work in offline mode
  • manages notifications

A closer look at Service Workers reveals that most of PWA’s heavy lifting actually happens in the browser. That’s why the first step to ensuring PWAs deliver the desired User Experience is to set the stage just right.

Let the show begin: Service Worker installation

As PWAs are an evolving technology, you might want to check for browser compatibility to make sure you can achieve the desired results. Other main prerequisites for implementing PWAs include hosting in a secure environment (HTTPS protocol) and experience in working with JavaScript Promises and event listeners. To make things easier, different browsers have tools for PWA developers you can use to quickly inspect and manage Service Workers.

Once you’ve agreed on the scope of the Service Worker (the paths where requests will be handled by the SW), you can begin installation. First, you need to register it in the main JavaScript code, so that the browser knows its location and can start installation in the background:

navigator.serviceWorker.register('/service-worker.js', {
  scope: '/app/'
});

Successful registration triggers the instal event. If Service Worker is installed without hiccups, its lifecycle begins…

Service Workers and their eventful lifecycles

Implementation of Service Workers can change with time, along with adding new features or updates. That’s why each Service Worker has its lifecycle, which corresponds to a series of key events triggering a given operating mode:

  1. Installation → Installed
  2. Activation → Activated / Idle
  3. Termination → Redundant (ready to be replaced by a new/updated SW)

The event-driven nature of Service Workers means that installation and activation processes trigger the corresponding install and activate events. Service Workers also make use of message events to communicate with other scripts, as well as functional events: fetch, push and synch.

When not in use, an activated Service Worker is idle until terminated or reactivated by a fetch or push event. In the case of high memory systems, SW can be allowed to run longer so that it can respond faster to the next event. Otherwise, it will be terminated after each event is complete.

Show must go on: Service Worker update

Whenever you need to update app logic or make more profound changes to the cache management, the old SW becomes redundant and a new SW has to be registered and installed. However, until that happens, the previous version of SW is still in charge of fetch events and the new SW will remain in waiting.

It’s important to remember that only one Service Worker for each scope can be in use at any given moment. That’s why a new SW can become active only after all pages and tabs using the old SW are closed.

With the waitUntil promise in place, all improvements and fixes have to be complete before the first fetch event is fired. This way, you can implement updates without affecting the user, giving your PWA another advantage over native apps.

Service Workers and caching without crashing

Progressive Web Apps can offer improved loading speed* and offline support by employing Service Workers to execute different caching strategies. In this context, a caching strategy defines the behaviour of the Service Worker upon receiving a fetch event.

*Note that when it comes to optimising app performance, it’s not only the overall speed that we’re fighting for here. An additional focus on Time to Interactive (TTI) can help you see when the core app features become available to users, even if not all page elements are fully loaded yet.

Workbox caching strategies

Implementing caching strategies might seem overwhelming when building a Service Worker from scratch. Fortunately, there are a number of libraries and tools to make your life much easier. Workbox stands out as a go-to resource for PWA developers. With handy Node modules, high-level JavaScript libraries and webpack plugins, it gives you everything you need to craft a solid foundation for caching, routing, and response logic.

The compilation below highlights key aspects of using Service Workers to cache assets and features the corresponding workbox-strategies.

Cache first + network fallback

How: If there is a cached response for a given request, the Service Worker will use that, without calling the network. Otherwise, it will use the network and cache the given response for future use.

Use case: non-critical resources, which don’t change often, e.g. font files.

Code snippet: import {CacheFirst} from 'workbox-strategies';

Network first + cache fallback

How: The SW will always call the network first and cache the fetched request; it will fallback to cache only if the network fails to return a response.

Use case: Frequently updated requests and whenever the latest version is important but also needed offline, e.g. server-side HTML responses.

Code snippet: import {NetworkFirst} from 'workbox-strategies';

Cache only

How: The SW will try to fetch resources only from the cache.

Use case: When fetching resources selected for precaching, e.g. a default offline page or a splash screen.

Code snippet: import {CacheOnly} from 'workbox-strategies';

Network only

How: The SW will try to fetch particular resources only from the network.

Use case: For pages where online live time is essential, e.g. an active user page and its content.

Code snippet: import {NetworkOnly} from 'workbox-strategies';

Stale-while-revalidate

How: The SW will call both the cache and the network. It will quickly fetch a cached response, fallback to network if necessary and then update the cache.

Use case: Loading times take priority over the latest version of page content, e.g. CSS files for a quick render of the initial page.

Code snippet: import {StaleWhileRevalidate} from 'workbox-strategies';

When Lie-Fi’s on, who’re you gonna call? Service Workers!

If you’ve already decided to switch from native mobile apps to Progressive Web Applications, then Service Workers should become your best dev buddies. Whenever Lie-Fi is looming on the horizon, make sure to have your caching strategies defined and configured to perfection. Now that you’ve made it this far, the land of more-than-optimal performance, offline reliability and exceptional User Experience is finally within reach.