Want more? Subscribe to my free newsletter:

Adaptive Serving using JavaScript and the Network Information API

October 10, 2018

navigator.connection.effectiveType is useful for delivering different assets based on the quality of the user's network connection.

effectiveType is a property of the Network Information API, exposed to JavaScript via the navigator.connection object. In Chrome, you can drop the following into DevTools to see your effective connection type (ECT):

console.log(navigator.connection.effectiveType); // 4G

Possible values for effectiveType are 'slow-2g', '2g', '3g', or '4g'. On slow connections this capability allows you to improve how quickly pages load by serving lower-quality versions of resources.

Before Chrome 62, we only exposed the theoretical network connection type to developers (via navigator.connection.type) rather than the network quality actually experienced by the client.

Chrome's implementation of effective connection type is now determined using a combination of recently observed round-trip times (rtt) and downlink values.

It summarizes measured network performance as the cellular connection type (e.g. 2G) most similar, even if the actual connection is WiFi. i.e. picture you're on Starbucks WiFi, but your actual effective network type is 2G or 3G.

What about responding to changes in network quality? We can use the connection.onchange event listener to monitor for connection changes:

function onConnectionChange() {
    const { rtt, downlink, effectiveType,  saveData } = navigator.connection;

    console.log(`Effective network connection type: ${effectiveType}`);
    console.log(`Downlink Speed/bandwidth estimate: ${downlink}Mb/s`);
    console.log(`Round-trip time estimate: ${rtt}ms`);
    console.log(`Data-saver mode on/requested: ${saveData}`);

navigator.connection.addEventListener('change', onConnectionChange)

Below is a quick test where I emulated a "Low-end mobile" profile in DevTools and was able to switch from "4g" to "2g" conditions:

effectiveType is supported in Chrome, Opera and Firefox on Android. A number of other network quality hints are available on navigator.connection, including rtt, downlink and downlinkMax.

An open-source project I've used effectiveType in was a Vue.js Google Doodles app. Using data-binding, we were able to set a connection property to either fast or slow based on ECT values. Roughly:

if (/\slow-2g|2g|3g/.test(navigator.connection.effectiveType)) {
  this.connection = "slow";
} else {
  this.connection = "fast";

This allowed us to conditionally render different output (a video vs. a low-res image) depending on the user's effective connection type.

      <div id="home">
        <div v-if="connection === 'fast'">
          <!-- 1.3MB video -->
          <video class="theatre" autoplay muted playsinline control>
            <source src="/static/img/doodle-theatre.webm" type="video/webm">
            <source src="/static/img/doodle-theatre.mp4" type="video/mp4">
        <!-- 28KB image -->
        <div v-if="connection === 'slow'">
          <img class="theatre" src="/static/img/doodle-theatre-poster.jpg">

Max Böck wrote an interesting article about network-aware components using React. He similarly highlighted how to render different components based on the network speed:

        switch(connectionType) {
            case '4g':
                return <Video src={videoSrc} />

            case '3g':
                return <Image src={imageSrc.hires} alt={alt} />

                return <Image src={imageSrc.lowres} alt={alt} />

Note: You can pair effectiveType with Service Workers to adapt to when users are offline in addition to slower effective connection types.

For debugging, you can override the network quality estimate using the Chrome flag "force-effective-connection-type" which can be set from chrome://flags. DevTools Network emulation can provide a limited debugging experience for ECT too.

effectiveType values are also exposed via Client Hints allowing developers to convey Chrome's network connection speed to servers.

Further reading on this feature, see:

I cross-posted this article to dev.to for folks that would also like to read or share it via there.