Performance #29 - Adaptive Loading — Network & Device-Aware Experiences

navigator.connection, deviceMemory, and hardwareConcurrency let you serve lighter experiences to constrained users. Here's how to use them without creating a degraded second-class experience.

8 min read
Performance
Network
UX

TABLE OF CONTENTS
Performance #29 - Adaptive Loading — Network & Device-Aware Experiences

Performance optimization typically targets the average user on a decent connection. But a significant portion of web traffic comes from low-end Android devices on 3G networks, where "fast on my MacBook" can mean "unusable in practice". Adaptive loading adjusts what you serve based on the actual capabilities of the user's device and network — not assumptions about them.


The Network Information API

navigator.connection exposes the user's network conditions. This API is currently only supported in Chromium-based browsers (Chrome, Edge, Opera, Samsung Internet). In Firefox and Safari, navigator.connection is undefined — always guard with optional chaining (?.) and provide sensible defaults for unsupported browsers.

The three signals you'll use most often:

You can also listen for changes — a user might move from WiFi to cellular mid-session:


The Device Memory API

Values below 1GB indicate a low-memory device — and low-memory devices are almost always low-CPU too. A device with 512MB of RAM will struggle with a 2MB JavaScript bundle, heavy animations, and multiple concurrent web workers.


Hardware Concurrency

Low core counts (1–2) indicate constrained CPUs. Avoid spawning many Web Workers on these devices — overhead exceeds benefit.


Practical Adaptive Loading Patterns

Here's a reusable helper that consolidates the connection signals into a single object:

Serve lower-quality images on slow connections

Disable non-essential animations on Save Data or slow networks

Defer non-critical JS on low-end devices

Reduce worker count based on CPU


CSS Media Query: prefers-reduced-data

The prefers-reduced-data media query (in progress, limited support) mirrors saveData for CSS:

Until browser support broadens, check navigator.connection.saveData in JavaScript and add a class to <html> to drive CSS variations:


prefers-reduced-motion

This is widely supported and critical for accessibility:

Some users disable motion for vestibular disorders, not just device constraints. Always respect this preference.


React Adaptive Hooks

The @react-hookz/web and similar libraries expose useNetworkState() and useDeviceMemory() hooks:


What Adaptive Loading Is Not

Adaptive loading is not a substitute for core performance work. A 5MB JS bundle is slow for everyone — serving 4.5MB to low-end devices doesn't fix the problem. Apply adaptive loading on top of a baseline that's already fast, to extend that fastness to the most constrained users.

It also shouldn't gate features in a way that creates a degraded "poor person's version" of the app. The goal is graceful adaptation — same features, less expensive delivery — not feature removal.

saveData is the one signal worth prioritising above all others — it's explicit user intent, not an inference. If someone has turned on Data Saver, that's a direct instruction. Everything else is a heuristic.


Let's Connect

© 2026 Naveen Karthik // Built with React & MUI