Dynamic resources using the Network Information API and service workers

My journey to and from work involves a small journey on a train. Without fail, there is this spot along the line where my network connection drops from a clear 4G signal to a 2G signal at best. I often kill time on the journey by surfing the web, and as you can imagine, a slow connection often stops bulky pages from loading altogether.

It’s worth mentioning that around the world, not everyone is lucky enough to get such good connection speeds. It’s crazy to believe that around 45% of mobile connections occur over 2G worldwide, with 75% of of connections occur on either 2G or 3G!

Loading most web pages over a 2G signal can be painfully slow, but up until now, developers didn’t have the means to accurately determine a users connection speed and adjust the content of their web pages accordingly. This is where the Network Information API aims to change things.

Network Information API

The Network Information API allows developers to determine the connection types and the underlying connection technology that the user agent is using. In terms of web performance, this brings a whole new level to the way that we build web pages. Being able to reliably determine whether or not a user has a slow 2G connection or a speedy Wifi connection allows developers to serve content accordingly. I am super excited about the difference this can make to mobile websites. In terms of web performance - this is HUGE! The best part about this API is that this information is exposed to service workers - allowing you to adjust content easily via the fetch API.

There are a number of use cases for the Network Information API. For example, if you are building a web application that allows for uploads or downloads, you might want to provide the user with the option to defer these actions if they are on a slower connection. Another example might be a web application that streams media to users; knowing their connection speed will allow you to adjust content or provide the user with this information.

In this article, we are going to be building a demo example that inspects each HTTP request to determine if the current user has a connection speed that is slower than 3G. If so, we will swap out the image in our service worker and return a lightweight placeholder instead. If the user has a connection speed that is 3G or better, we will return as normal and the user will be none the wiser. The image below gives you an idea of this in action.

Network Information API - Before and after

The basics

To give you a flavour for the information returned in this API, let’s open up any web page in Google Chrome and head over to the Console tab. Next type navigator.connection and you should be presented with something similar to the image below.

Chrome Developer Tools - Network Information API

Let’s break this information down in order to get a better understanding.

  • The downlink attribute represents an upper bound on the downlink speed of the first network hop and is reported in megabits per second.
  • EffectiveType is an estimation that is based on the round-trip time and downlink properties. This value can be represented in either slow-2G, 2G, 3G or 4G.
  • The rtt attribute is the effective round-trip time estimate in milliseconds
  • The saveData attribute returns true if the user has requested a reduced data usage mode from the user agent. I have previously written about using this value from HTTP request headers with service workers, but this is a much nicer way to retrieve this value in my opinion!

Of all of the values represented above, the key attribute that we will be using in this demo example will be the effectiveType value. We are going to be reading this value and using it to determine whether or not to adjust the quality of the image on the page.

Getting started

Enough with the talking - let’s get started with some code!

As an example page, I’ve created the following HTML.


<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>Connection Type</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
   <img src="./img/dog.jpg" width="400" height="400" />
</body>
<script>
   if ('serviceWorker' in navigator) {
       navigator.serviceWorker.register('./service-worker.js');
   }
   else {
       console.log('Service Worker functionality not supported.');
   }
</script>
</html>

The HTML code above creates a simple web page that displays an image of a dog. At the bottom of the page, I am registering a service worker that will contain the logic to determine the users connection speed and dynamically adjust the content we serve to the user.

Next, I am going to create a file called service-worker.js and add the following code.

"use strict";

self.addEventListener('fetch', function (event) {

    // Check if the current request is 2G or slow 2G
    if (/\slow-2g|2g/.test(navigator.connection.effectiveType)) {
        // Check if the request is for an image
        if (/\.jpg$|.png$|.gif$|.webp$/.test(event.request.url)) {

            // Return no images
            event.respondWith(
                fetch('placeholder.svg', {
                    mode: 'no-cors'
                })
            );
        }
    }
});

In the code above, we are tapping into the fetch event in order to assess the incoming HTTP requests. First up, we are checking to see if the user has a 2G or a slow-2G connection speed using navigator.connection.effectiveType. If they do, we then check to see if the current request is for an image, and if so, we simply return our placeholder image.

The placeholder image used in this example is a tiny SVG file that weighs in at around 95 bytes. The SVG image in this example is just a grey placeholder, but depending on your preferences, you could return a really low quality version of each image on your site, or even use Low Quality Image Placeholders (LQIP) instead.

That’s it! The code above will dynamically adjust the quality of the image that is returned depending on whether or not your users are on a low quality network connection. The best part about this is that users on older browsers will be none the wiser - this code simply won’t be executed for them.

Show me the money!

If you’d like to try this example out for yourself, I have created a demo page which is available at deanhume.github.io/pwa-connection-type. If you’d like to see all of the code used in this example, please head over to github.com/deanhume/pwa-connection-type.

In order to test this in your browser, you can use the network throttling functionality built into Google Chrome. If you open up the Developer Tools and select the Network tab, you can simulate different network connection speeds by selecting from the drop down.

Chrome Developer Tools - Network Throttling

While I was writing this article, I suddenly realised that this feature could be very useful for readers of this blog. If you visit this very site on a 2G connection, any images will be replaced with a placeholder, in order to allow you to focus on the contents of the article instead. Go ahead and reload this page with 2G throttling enabled...go on...I dare ya!

Dean Hume Blog - Network Information API

Browser Support

At the time of writing this article, only Google Chrome and Samsung Browser support this feature with support for other browsers on the way. However, the code above uses progressive enhancement to take this into account - it will only work on browsers that support it.

For more information on browser support, please see the table below.

Can I Use netinfo? Data on support for the netinfo feature across the major browsers from caniuse.com.

Summary

The Network Information API opens up a new world of possibilities for web developers. If you are looking for a simple way to determine the quality of your users connection speeds and adjust content accordingly, then this is the API for you.

If you’d like to learn more about the Network Information API, I recommend reading the WICG proposal as well as the docs on the Mozilla Developer Network.

Whilst I was researching for this article, I also came across an article entitled Connection Aware Components about building components for the web that adapt by using the Network Information API - I highly recommend reading it.