Learn how to ensure every user gets the best possible experience by optimizing your sites for specific hardware and network constraints.
Device capabilities and network connections vary a lot. Sites that delight users on high-end devices can be unusable on low-end ones. Sites that load smoothly on fast networks can come to a halt on slow ones. Any user can experience a slow website, that's why developing "one-size fits all" solutions may not always work.
In their Chrome Dev Summit talk, Addy Osmani from Google and Nate Schloss from Facebook explore a solution to that problem—a pattern for delivering pages that better cater to a variety of user constraints. They call it adaptive loading.
What is adaptive loading?
Adaptive loading involves delivering different experiences to different users based on their network and hardware constraints, specifically:
A fast core experience for all users (including low-end devices).
Progressively adding high-end-only features, if a user's network and hardware can handle it.
By optimizing for specific hardware and network constraints you enable every user to get the best possible experience for their device. Tailoring the experience to users' constraints can include:
Serving low-quality images and videos on slow networks.
Throttling the frame-rate of animations on low-end devices.
Avoiding computationally expensive operations on low-end devices.
Blocking third-party scripts on slower devices.
Loading non-critical JavaScript for interactivity only on fast CPUs.
Browser support and how to implement adaptive loading
The signals you can use for adaptive loading are listed below. Browser support is also included for each signal:
Navigator.deviceMemory
The navigator.deviceMemory
property is used to reduce memory consumption on low-end devices.
Navigator.hardwareConcurrency
The navigator.hardwareConcurrency
property is the CPU core count. It is used to limit costly JavaScript execution and reduce CPU intensive logic when a device can't handle it well.
NetworkInformation.effectiveType
The navigator.connection.effectiveType
property is used to fine-tune data transfer to use less bandwidth.
NetworkInformation.saveData
The navigator.connection.saveData
property is used to leverage the user's Data Saver preferences.
There are two places where you can make a decision about what to serve to users: the client and the server. On the client, you have the JavaScript APIs noted above. On the server, you can use client hints to get insight into the user's device capabilities and the network they're connected to.
Adaptive loading in React
React Adaptive Loading Hooks & Utilities is a suite for the React ecosystem that makes it easier to adapt your sites to lower-end devices. It includes:
The
useNetworkStatus()
hook for adapting based on network status (slow-2g
,2g
,3g
, or4g
).The
useSaveData()
hook for adapting based on the user's Data Saver preferences.The
useHardwareConcurrency()
hook for adapting based on the number of logical CPU processor cores on the user's device.The
useMemoryStatus()
hook for adapting based on the user's device memory (RAM).
Each hook accepts an optional argument for setting the initial value. This
option is useful in two scenarios: when the user's browser does not support the
relevant API and for server-side rendering where you can use the client hint
data to set the initial value on the server. For example, the
useNetworkStatus()
hook can use the initial value passed from client hint for
server-side rendering and, when executed on the client, update itself if the
network effective type changes.
React Adaptive Loading Hooks & Utilities are implemented using web platform APIs (Network Information, Device Memory and Hardware Concurrency). You can use the same APIs to apply adaptive loading concepts to other frameworks and libraries, such as Angular, Vue, and others.
Adaptive loading in action
This section explores demos of how you could use adaptive loading and real-world examples from sites such as Facebook, eBay, Tinder, and others.
The React Movie demo shows how to adapt media serving based on the network status. It's an application for browsing movies that shows posters, summaries, and cast lists. Based on the user's effective connection type, it serves high-quality posters on fast connections and low-quality posters on slow ones.
Twitter has a Data Saver mode designed to reduce the amount of data used. In this mode, preview images load in low-resolution and large images load only when you tap on the preview. With this option enabled, users on iOS and Android saved 50% in data-usage from images, and users on the web saved 80%. Here's a React demo that uses the Save Data hook to replicate the Twitter timeline. Try opening your DevTools Network panel and looking at the difference in the amount of data transferred as you scroll while Save Data is disabled versus when it's enabled.
eBay conditionally turns on and off features like zooming when a user's hardware or network conditions don't support them well. You can achieve this through adaptive code-splitting and code loading—a way to conditionally load more highly interactive components or run more computationally heavy operations on high-end devices, while not sending those scripts down to users on slower devices. Check out the video at 16 mins where Addy shows this pattern implemented with React.lazy() and Suspense on a demo eBay product page.
Tinder is using a number of adaptive loading patterns in its web and Lite app to keep the experience fast for everyone. If a user is on a slow network or has Data Saver enabled, they disable video autoplay, limit route prefetching and limit loading the next image in the carousel to loading images one at a time as users swipe. After implementing these optimizations, they've seen significant improvements in average swipe count in countries such as Indonesia.
![A screenshot of two versions of Tinder chat: with autoplaying video and with a video with play button overlay. A screenshot of a Tinder profile with caption 'Limit carousel images on Data Saver or 3G'. A code snippet for prefetching in-viewport videos only on 4G.](https://web.developers.google.cn/static/articles/adaptive-loading-cds-2019/image/a-screenshot-two-version-30856999784af.png?authuser=8)
Adaptive loading at Facebook
One issue that comes up in adaptive loading is grouping devices into high-end and low-end classes based on available signals. On mobile devices the user-agent (UA) string provides the device name which enables Facebook to use publicly available data on device characteristics to group mobile devices into classes. However, on desktop devices the only relevant information the UA provides is the device's operating system.
For grouping desktop devices, Facebook logs the data about the operating system,
CPU cores (from navigator.hardwareConcurrency
), and RAM memory
(navigator.deviceMemory
) in their performance monitoring. Looking at the
relationships between different types of hardware and performance, they
classified devices into five categories. With hardware classes integrated into
performance monitoring, they get a more complete picture of how people use
Facebook products depending on their device and can identify regressions more
easily.
Check out the video at 24 mins, where Nate walks through how Facebook approaches device grouping and uses adaptive loading for animations and loading JavaScript.
Learn more about adaptive loading
Adaptive loading is all about designing your sites with inclusivity in mind. Build a core experience that works great for everyone, then toggle or layer features that make it even more awesome if a user has enough memory, CPU, or a fast network. To learn more about adaptive loading, check out the available demos and watch the Chrome Dev Summit talk: