How to create the best experience for every user with the newest web APIs

We are building the web on premium devices, fast internet connections and data flat rates. That means that the consequent pages built work well for us but don’t work as well for most of our users. This will become an even bigger problem for the next billion users that can only afford cheap devices and rely mainly on cellular connections.

To provide a good experience for every user we have to make smart decisions based on:

Until now this has been very hard to do. However, thanks to exiting new web APIs this could change in the near future. In this article I will explore these new APIs and share my best practices with you.

User preferences — respect the user

Wouldn’t it be great to get hints from the user what she or he prefers? With the new HTTP Client Hints this is finally possible.

In Google Chrome and Opera the users can already set the save-data option. This is a great way for users to say: please don’t waste my data.

There are good reasons for users to save data:

The user hints are added as a http request header and are also available via a Javascript API (navigator.connection.saveData).

Save-data header

Most of the projects I work on use aggressive caching strategies. Using the save-data header allows you to deliver a pre-rendered save-data version of your page, which is my preferred approach:

// false by default.
$saveData = false;
// Check if the `Save-Data` header exists and is set to a value of "on".
if (isset($_SERVER["HTTP_SAVE_DATA"]) && strtolower($_SERVER["HTTP_SAVE_DATA"]) === "on") {
// `Save-Data` detected!
$saveData = true;

Save-data Javascript API

You can also use the Javascript API to make decisions on the client if necessary:

if ("connection" in navigator) {
  if (navigator.connection.saveData === true) {
      // Implement data saving operations here.

Best practices for save-data:

In case of a conflict between best practices (for example: avoid lazy loading on a cellular connection), I would always prioritise approaches that save data.

Respect the user: always make user preferences the highest priority.

Browser support

In June 2018 this is supported by Google Chrome (from version 46) and Opera (from version 33). Check out the current browser support.

More resources

Network — load quickly

The network type, latency, and bandwidth all have a great influence on the user experience. Unfortunately it can be hard to detect the network type and the current latency and bandwidth.

However, with the new Network Information API this might change soon.

The Network Type

To be able to make smart decisions it is important to know the current network type (ethernet, wifi, and cellular).

Ilya Grigorik did a great job explaining the differences between the network types in his book “High Performing Networking”, which I highly recommend.

I want to focus here on the cellular connections as they are the most challenging ones when it comes to latency and bandwidth.

Source: Ilya Grigorik, High Performance Browser Networking

Cellular connections are managed by the Radio Resource Controller which is part of the mobile network. To be able to serve as many users as possible it tries to minimize the connection times for each user. This is done by sending the devices into idle mode as soon as possible. As the mobile device radios are the second biggest power consumer, this also helps to save battery power which is a good thing.

With the Network Information API we can check for the connection type.

// Get the connection type.
var type = navigator.connection.type;
// Result: 'bluetooth', 'cellular', 'ethernet', 'none', 'wifi', 'wimax', 'other', 'unknown'

Effective Type

The network type alone doesn’t tell you much about the network latency and bandwidth. If you have ever used the wifi in a hotel or train, you’ll know what I am talking about.

The effective type tells you how fast the current connection is. It uses the values ‘slow-2g’, ‘2g’, ‘3g’, or ‘4g’ to describe the current speed. If the result is ‘slow-2g’ it doesn’t mean the network type is 2g, but that the network behaves like a slow 2g network, taking the latency and bandwidth into account.

// Get the connection type.
var type = navigator.connection.effectiveType;
// Result: 'slow-2g', '2g', '3g', or '4g'

Observing changes

Network connections are prone to change. To be able to adapt you’ll need to be aware of changes. This is where the onchange property comes in handy.

// Register for event changes.
navigator.connection.onchange = changeHandler;

Best practices

If the connection type is cellular I propose to follow these best practices:

1. Load as much as possible and go idle (warning: don’t use any of these best practices if the save-data header is set):

  1. Collect user data and send it via the Beacon API:

If the current network is very slow (slow-2g or 2g) you might want to consider following the best practices we already discussed in the save-data section.

Browser support

At the time of writing the Network Information API is still experimental. Check out the current browser support.

Test your websites and apps constantly on slow connections.

Device — run smoothly

When it comes to devices, we have to consider two trends:

The gap between premium and cheap devices is becoming bigger with each iteration. his might become the next big challenge for web developers.

This graphic compares the same page rendered on different devices. The Scripting and Rendering takes considerably longer on less powerful devices.

How to detect the device capability?

The “Cut the mustard” method is often used to decide whether to load a simple or full experience of a website. This is done by simply checking if the browser supports features which are only supported by modern browsers.

However, as modern browsers can also run on old devices the “Cut the mustard” method is far from being perfect.

A better approach could be to check the Device Memory and hardwareConcurrency:

// Get the connection type.
var type = navigator.hardwareConcurrency;

The hardwareConcurrency is the number of logical processors potentially available to the user agent.

// Get the connection type.
var type = navigator.deviceMemory;

The deviceMemory returns how much RAM the device has in gigabytes, rounded down to the nearest power of two. The API also features a Client Hints Header.

Battery status

The battery status of an device can also influence the user experience. To save battery, the save-data best practices can be applied. Additionally I would avoid all animations.

The battery status can be retrieved via the Battery Status API. Warning: the Battery Status API is not longer supported by webkit and Firefox due to privacy reasons.

Best practices for slow devices

There are other great APIs which help you to control when and where your Javascript code is running. This is beneficial for all kinds of devices. If these concepts are new to you I would recommend to read the linked articles from Paul Lewis:

This will make your non-critical Javascript run only during idle time, which is great as it no longer blocks user requests.

Browser Support

Test your websites and apps constantly on old and cheap devices.

New: Xperience Cards Workshop

At Netcentric we offer a brand new one day workshop which covers all these topics and more. The workshop helps the project team to deliver the best user experience for every user. Stay tuned for more information on this.