Connection-Aware Components

Over the last decade, we have learned to embrace the uncertainty of developing for the web.

We don’t design sites for specific screen dimensions anymore, we make them responsive. We don’t assume ideal browsers and devices, we use progressive enhancement. When it comes to connectivity though, we still treat that as a binary choice: you’re either on- or offline.

Real connections are not that simple. Depending on your location, network condition or data plan, speeds can range from painfully slow to blazingly fast. The concept of “online” can be a drastically different experience for different users, especially on mobile.

What if there was a way to adapt websites based on our users connections, just like we do for varying display widths and browser capabilities? The Network Information API might enable us to do so.

The Network Information API

This API is an editor’s draft by the WICG and currently available in Chrome. It can be accessed through the read-only property navigator.connection (MDN), which exposes several properties that provide information about a user’s current connection:

  • connection.type:

Returns the physical network type of the user agent as strings like “cellular”, “ethernet” or “wifi”.

  • connection.downlink:

An effective bandwidth estimate (in Mb/s), based on recently observed active connections.

  • connection.rtt:

An estimate of the average round-trip time (in milliseconds), based on recently observed active connections.

  • connection.saveData:

Returns true if the user has requested “reduced data mode” in their browser settings.

  • connection.effectiveType:

This is a combined estimation of the network quality, based on the round-trip time and downlink properties. It returns a string that describes the connection as either: slow-2g, 2g, 3g or 4g. Here’s how these categories are determined:

Responding to Changes

There is also an Event Listener available on the connection property that fires whenever a change in the network quality is detected:

function onConnectionChange() {
const { rtt, downlink, effectiveType } = navigator.connection
console.log(`Round Trip Time: ${rtt}ms`)
console.log(`Downlink Speed: ${downlink}Mb/s`)
console.log(`Effective Type: ${effectiveType}`)
}
navigator.connection.addEventListener('change', onConnectionChange)

Support

can I use support table for the network information API

👉 Be aware that all of this is still experimental. Only Chrome and Samsung Internet browsers have currently implemented the API. It’s a very good candidate for progressive enhancement though - and support for other platforms is on the way.

Connection-aware components

So how could this be used? Knowing about connection quality enables us to custom-fit resources based on network speed and data preferences. This makes it possible to build an interface that dynamically responds to the user’s connection - a “connection-aware” frontend.

By combining the Network Information API with React, we could write a component that renders different elements for different speeds. For example, a <Media /> component in a news article might output:

  • offline: a placeholder with alt text
  • 2g / reduced data mode: a low-resolution image, ~30kb
  • 3g: a high resolution retina image, ~200kb
  • 4g: a HD video ~1.8MB
a media component, showing four different states of an image or video of a chameleon
The different states of our Media component

Here’s a (very simplified) example of how that might work:

class ConnectionAwareMedia extends React.Component (
constructor(props) {
super(props)
this.state = {
connectionType: undefined
}
}

componentWillMount() {
// check connection type before first render.
if (navigator.connection && navigator.connection.effectiveType) {
const connectionType = navigator.onLine
? navigator.connection.effectiveType
: 'offline'
this.setState({
connectionType
})
}
}

render() {
const { connectionType } = this.state
const { imageSrc, videoSrc, alt } = this.props

// fallback if network info API is not supported.
if (!connectionType) {
return <Image src={imageSrc.hires} alt={alt} />
}

// render different subcomponents based on network speed.
switch(connectionType) {
case 'offline':
return <Placeholder caption={alt} />

case '4g':
return <Video src={videoSrc} />

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

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

Using a Higher-Order Component

The above example makes our component a bit unpredictable - it renders different things, even when given the same props. This makes it harder to test and maintain. To simplify it and enable reuse of our logic, moving the network condition check into a separate higher-order component might be a good idea.

Such a HoC could take in any component we want and make it connection-aware, injecting the effective connection type as a prop.

function withConnectionType(WrappedComponent, respondToChange = false) {
return class extends React.Component {
constructor(props) {
super(props)
this.state = {
connectionType: undefined
}
// Basic API Support Check.
this.hasNetworkInfoSupport = Boolean(
navigator.connection && navigator.connection.effectiveType
)
this.setConnectionType = this.setConnectionType.bind(this)
}

componentWillMount() {
// Check before the component first renders.
this.setConnectionType()
}

componentDidMount() {
// optional: respond to connectivity changes.
if (respondToChange) {
navigator.connection.addEventListener(
'change',
this.setConnectionType
)
}
}

componentWillUnmount() {
if (respondToChange) {
navigator.connection.removeEventListener(
'change',
this.setConnectionType
)
}
}

getConnectionType() {
const connection = navigator.connection
// check if we're offline first...
if (!navigator.onLine) {
return 'offline'
}
// ...or if reduced data is preferred.
if (connection.saveData) {
return 'saveData'
}
return connection.effectiveType
}

setConnectionType() {
if (this.hasNetworkInfoSupport) {
const connectionType = this.getConnectionType()
this.setState({
connectionType
})
}
}

render() {
// inject the prop into our component.
// default to "undefined" if API is not supported.
return (
<WrappedComponent
connectionType={this.state.connectionType}
{...this.props}
/>

)
}
}
}

// Now we can reuse the function to enhance all kinds of components.
const ConnectionAwareMedia = withConnectionType(Media)

👉 This small proof-of concept is also available on CodePen, if you want to play around.

Further Reading

Webmentions

  1. Ropsu Ehn
    WTF, seriously can?? twitter.com/mxbck/status/1…
  2. Peter Hoddie
    Hey @midodd, didn't we do something like this in about 1999?
  3. Ryan Miglavs
    Cool!
  4. Matthias Götzke
    Nice tech, but it assumes that people with LTE have unlimited data plans, at least here in Germany it is often limited and I would not want to waste it on videos unless I opt in. And the 'data saver' features don't seem to work with the Browser APIs in that sense or do they?
  5. Max Böck
    The API does provide a `connection.saveData` property that can help with that. you could serve users in data saver mode smaller assets per default, and include an option to show the video if they prefer.
  6. Lewis Pugsley
    Be interesting to test this from a CRO perspective.
  7. dhoko
    Perso j'avais monté une solution avec window.performance.getEntriesByName et un benchmark sur un fichier de l'app. 4 formats suivant la vitesse de co + latence: dsl 4G 3G+ 3G 2G Je pouvais jouer sur la résolution + qualité de l'image.
  8. Sexy Anatoly
    If i am used ssr, what now?
  9. Max Böck
    client hint headers might be available in the future. there's currently a feature flag in chrome: chromestatus.com/feature/540790…
  10. SΞbastien F4GRX
    3g in good rf conditions can be faster than 4G in bad rf conditions, this idea is shit. always show an image with a video link.
  11. Alex Moldovan
    Very interesting take on the future of #webperf at component level by @mxbck mxb.at/blog/connectio… 🚀
  12. Johannes Neukamm
    @mediamicx - darauf hattest Du doch IMMER gewartet, nech? 😍🚀
  13. Dean Hume
    Connection-Aware Components mxb.at/blog/connectio… #webperf #perfmatters
  14. Andrew Secret
    Interesting article about connection aware components mxb.at/blog/connectio… 💨⚡️
  15. Lucasoft co.uk
    RT @Real_CSS_Tricks Connection-Aware Components :: mxb.at/blog/connectio… (Did you know the Network Information API (new) can cough up data like how fast the users bandwidth is?) #tech #engineering
  16. HJ Chen
    this is an exciting prospect, and another dimension to think about when using the web as our content-delivery medium of choice #webdev #performance #embracetheweb
  17. Will ᕙ(⇀‸↼‶)ᕗ
    I’m planning on making this happen this week in Stellar!! 😱😱
  18. WarEnek
    То о чём давно мечтал
  19. Craig Taub
    "Connection-aware components" - Really like seeing the new native API's used in the wild. Solving real UX problems in a concise + idiomatic fashion 👍. #nodejs #reactjs #vuejs #javascript #es6 #graphql #webdev mxb.at/blog/connectio…
  20. For Web
    Пример использования Network Information API для разработки компонентов, ведущих себя по-разному в зависимости от скорости интернета → mxb.at/blog/connectio…
  21. Martin Michálek
    Komponenty, které se přizpůsobují rychlosti připojení. Krásná představa, jen škoda, že Network Information API má slabou a navíc jen experimentální podporu. mxb.at/blog/connectio…
  22. HsiangHui
    Connection-Aware Components #webperf mxb.at/blog/connectio…
  23. あほむ
    Connection-Aware Components | Max Böck - Frontend Web Developer mxb.at/blog/connectio… `navigator.connection` の概説と、それを利用した <ConnectionAware> React コンポーネントの実装例
  24. azu
    見てる: "Connection-Aware Components | Max Böck - Frontend Web Developer" mxb.at/blog/connectio…
  25. azu
    Network Information APIや`navigator.onLine`などを使った接続情報を考慮したReactコンポーネントの作り方について "Connection-Aware Compone…" mxb.at/blog/connectio…
  26. JSer.info
    "Connection-Aware Components | Max Böck - Frontend Web Developer" realtime.jser.info/2018/07/19/con… => mxb.at/blog/connectio… Network Information APIやnavigator.onLineなどを使った接続情報を考慮したReactコンポーネントの作り方について
  27. Connection-Aware Components | Max Böck - Frontend Web Developer mxb.at/blog/connectio…
  28. Drag N Drop
    What if there was a way to adapt websites based on our users connections? @mxbck talking about The Network Information API mxb.at/blog/connectio…
  29. Olivier Guilleux
    Brace yourself something cool has arrived... only on Chrome 😪 mxb.at/blog/connectio…
  30. Paulo Elias
    "Connection-Aware Components" mxb.at/blog/connectio… by @mxbck Demo: codepen.io/mxbck/pen/JZxy…
  31. Manel García
    Network speed aware ⚛️ #React components. 👇 mxb.at/blog/connectio…

Featured Blog

Mina Markham

http://mina.codes

Mina is a front-end architect based in Brooklyn, New York. She's responsible for 'Pantsuit', the design system used in Hillary Clinton's presidential campaign. Her blog is a great resource on topics like Sass, Typography and Graphic Design.

Read More