Going Offline With Service Workers

The average global internet speed is getting faster year by year, but the number of users with a slow 3G connection is still in the millions. The number of mobile internet users is growing and it’s already surpassed desktop usage worldwide a long time ago. Internet connections for these users can be unreliable and sometimes even non-existent. Often they may want to access your content on the go and if you want to provide a seamless user experience — with these people in mind — you have to consider going offline.

Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
JavaScript Course

Offline Apps

Offline apps try to tackle these connectivity problems by introducing some clever technical solutions, such as

Detecting offline state

To make your web app work offline, you first need to determine your user’s connection status. This can be done with the online and offline events and with the help of navigator.onLine.

Storing data and assets offline

This is where a service worker can shine. You want to store static assets locally in the cache, so your app can load even when the internet connection is down. It also helps your site load faster as it requests the resources from the cache rather than making a network request.

For storing data locally, there are a couple of options but your best bet is using localStorage. You can also use it to queue requests while offline and call them once the user is back online.

Sync with server

Once the connection is restored, you can sync with the server and save data back that’s been queued during the offline state.

Diagram showing the lifecycle of an offline app

Prerequisites

When it comes to using service workers, there are some prerequisites that must be met. First, we have to point out that it can still be considered an experimental feature, therefore before you use it, you have to look into browser support. It is growing constantly however and only IE is unsupported at this moment. For full coverage, you can refer to the compatibility table at caniuse.com

The other requirement is that your site must be served through HTTPS. This is because of security reasons as service workers act as a proxy that can alter your responses. Therefore it’s essential to ensure that a service worker hasn’t been tampered with. You can still use them locally however, during development.


Lifecycle of a Service Worker

A service worker has a lifecycle that is separate from your app. It runs on a different thread, therefore it’s important to mention that you won’t be able to access the dom and do things you would normally do in your everyday JavaScript files, but service workers are not meant for that anyway.

Register

To add a service worker for the app, you first need to register it in one of your JavaScript files. Once registered, it will cause the browser to start the next step in the background which is:

Install

During installation, you usually want to cache static assets on your site. If everything goes well and all your files are downloaded successfully, then the service worker becomes installed. If any of those files fail to download for some reason, then this step will fail and the service worker won’t be installed. In that case, it will try again next time.

Activated

After the installation step is completed, the service worker becomes activated. In this step, you usually want to update your worker; remove, or update old cache files.

Fetch

Once activated, your service worker can listen for fetch events that will be fired whenever a network request is made. During this time, you can intercept these requests and return the cached version if there’s any or continue with the network request if there’s none.

Terminated

When not in use anymore, the service worker will be terminated to save memory and only becomes available when it’s next needed.

These are the steps we need to go through during implementation, so let’s start by registering it first.


Registering a Service Worker

Registration can be done in one single line:

navigator.serviceWorker.register('sw.js');
app.js
Copied to clipboard!

Where sw.js is the location of the JavaScript file, which holds the functions that the service worker needs to perform. A common convention is to create the service worker’s JavaScript file at the root directory of your project, but it can live anywhere as you can pass the path to the register method.

As discussed previously, they are not yet supported in all browsers so it’s a good practice to check if the serviceWorker object exists on navigator. Checking for support can be done with:

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('sw.js');
}
app.js
Copied to clipboard!

Installation

Inside the service worker file, you can refer to the object itself with the self keyword. Looking back at the lifecycle, we can see the very first step we need to take is installing it. Luckily we have handy events for which we can attach event listeners to. To install a service worker we have to attach a listener to the install event like so:

self.addEventListener('install', (event) => {
    // This is where we will cache our files
});
sw.js
Copied to clipboard!

Everything that goes inside the callback function will get executed whenever the service worker gets installed for the very first time. This is where you want to add files to your cache. To do so, we can extend the install event with the following:

self.addEventListener('install', event => {
    event.waitUntil(
        caches.open('sw-cache').then(cache => {
            return cache.addAll([
                '/',
                '/assets/css/main.css',
                '/assets/js/app.js',

                '/assets/img/favicon.ico',
                '/assets/img/bg.jpg',

                '/assets/fonts/Raleway-Bold.ttf',
                '/assets/fonts/Raleway-Regular.ttf'
            ]);
        })
    );
});
sw.js
Copied to clipboard!

Starting from the inside out, cache.addAll takes in an array of files that we would like to cache. A great rule of thumb is to cache only static assets that rarely change, like fonts and images.

We return that to caches.open, which takes in a chain of promises, which cache.addAll returns. The sw-cache string is the name of the cache to use, it can be anything else.

Then we wrap everything inside event.waitUntil. It takes in a promise which is used to know how long the installation takes. If any of the files inside cache.addAll fails to download, the installation step will fail.


Returning The Cache

All that’s left to do is intercepting the requests and returning their cached version. For that, we can listen for the fetch event.

self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request).then(response => {
            return response || fetch(event.request);
        })
    );
});
sw.js
Copied to clipboard!

Whenever the user navigates to a page that has a service worker installed, it will start to receive fetch events from each request. Here we use event.respondWith and pass in a promise: caches.match, which looks at the request, and if a match is found, it returns the cache created by the service worker. Otherwise, it uses the fetch function to request the resource over the network and return the value from the function call. This is equivalent to saying:

if (response) {
    return response;
} else {
    return fetch(event.request);
}
sw.js
Copied to clipboard!

All that’s left to do is to test it out. You can do so by opening the DevTools in Chrome and changing the Online state to Offline, inside the network tab. Refresh the page and appreciate how your site still loads without trouble.

Setting up network connection in DevTools

It’s also a good time to verify that in case of slow connection it should still load in a breeze. 🐌

JavaScript Course Dashboard

Tired of looking for tutorials?

You are not alone. Webtips has more than 400 tutorials which would take roughly 75 hours to read.

Check out our interactive course to master JavaScript in 5 hours.

Learn More

📚 Get access to exclusive content

Want to get access to exclusive content? Support webtips with the price of a coffee to get access to tips, checklists, cheatsheets, and much more. ☕

Get access Support us
Read more on
🎉 Thank you for subscribing to our newsletter. x