JavaScript Network Information API Table

PieceWhat it doesField note
navigator.connectionThe objectNetworkInformation - Chromium only; Safari/Firefox/iOS absent, and the undefined branch is your default path
effectiveTypeThe bucket label'slow-2g' to '4g' from RTT/downlink bands - stadium Wi-Fi and fiber share a label; a dial, not a gate
downlink / rttThe estimatesQuantized (rtt snaps to 25ms), capped, and lagging reality by seconds - dashboards, not switches
saveDataThe user statementThe only field that is intent, not inference: user asked to spend less - defer prefetch, lean media
change eventThe live updateRe-read on change instead of polling - the browser updates as traffic accrues, slowly
where numbers come fromThe honestyRecent traffic + carrier reporting - freshly-woken connections report the pre-sleep network
what it is forThe decisionMedia quality floors, prefetch budgets, sync deferral - never feature gates or UX promises
the fallbackThe measurementNavigation/Resource Timing = your users' real RTT and throughput - works on every engine
Reference: the MDN Network Information API. The browser's guess about the connection: four fields - one of them (saveData) a user statement, three of them heuristics derived from recent traffic - and a change event to catch the updates.
Bottom line: branch on saveData first - it is consent, the only field that never needs a second opinion. Treat effectiveType as a quality dial (media floors, prefetch budgets), never a gate, because the buckets put stadium Wi-Fi and fiber in the same label. And expect the object to be undefined on half your audience: Safari, Firefox and every iOS browser ship nothing - so the design that works without the API (adaptive defaults, lazy loading, server compression) is the one you should have anyway, improved by the API where it exists.
Related tools: the fetch table (the requests the budgets govern), the visibility table (the other pause-and-defer signal), the service worker table (the offline half of lean networking), and the performance timing table (the real-user measurement the API sketches).

The Network Information API exposes the browser's guess about your connection: navigator.connection (when it exists) carries effectiveType - a bucket label like '3g' or '4g' - plus downlink (megabits estimate), rtt (round-trip estimate), and saveData, the user's explicit data-saver preference. Chrome and Edge ship it; Safari and Firefox do not, which makes the API's first design question what your page does when the object is simply undefined.

Bottom line: one field of the four is a user STATEMENT, and it is the only one worth branching on. saveData: true means the user asked to spend less - defer prefetches, downshift media, skip autoplay; that is consent, not a guess. The other three are inferences the browser derived from recent traffic and carrier reporting - coarse, slow-updating, and wrong in both directions (a hotel Wi-Fi reads '4g' at 40ms that feels like 3G; a fast 5G read '2g' right after waking from idle). Bucket the behaviors, never the user: media quality floors, prefetch budgets, sync deferral - not feature gates.

The honest part: the numbers are heuristics wearing decimal points. downlink is capped and quantized, rtt is reported in 25ms granularity from RECENT traffic (meaning: a freshly-woken connection reports stale data), and effectiveType compresses all of that into four bands that put a crowded stadium on the same label as a fiber line. And on the two engine families without the API - which includes every iOS browser - there is no signal at all. So the production pattern: read the API when present, measure real users with Navigation and Resource Timing for the truth, and ship adaptive-by-default designs that work without knowing anything.

How to use

  1. Feature-detect the object: const conn = navigator.connection; if (conn) { ... } - absent on Safari, Firefox, and all iOS browsers; the undefined branch is your app's default path, not an error state.
  2. Branch on saveData first: if (conn.saveData) skip the hero video, hold prefetches, prefer text - this is the user's own request, the only field that never needs a second opinion. Pair it with prefers-reduced-data CSS media queries where the platform supports them.
  3. Use effectiveType as a quality dial, not a gate: map buckets to media floors (4g = default, 3g = SD, 2g = thumbnails + text) - and re-read on conn.addEventListener('change', ...) instead of polling, since the browser updates estimates as traffic accrues.
  4. Treat downlink and rtt as dashboards, not switches: they move in quantized steps (downlink maxes out, rtt snaps to 25ms multiples) and lag reality by seconds; logging them alongside your own timing data teaches you your audience - gating UX on a single reading teaches your users to distrust the app.
  5. Measure for real when the API is missing: Navigation Timing's responseStart minus requestStart is your users' actual RTT; Resource Timing sizes and durations are their actual throughput - a 50-user sample of those beats any connection label, and it works on every browser including the ones that hide navigator.connection.

Frequently asked questions

Why does effectiveType say 4g on a network that feels slow?

Because effectiveType is a band derived from thresholds, not a speed test: '4g' covers everything from fiber to a stadium's overloaded cell tower, as long as recent RTT and downlink estimates land in the top band. The label compresses two dimensions (latency and bandwidth) into four buckets that were never re-tuned for modern networks - 5G, satellite, and captive-portal Wi-Fi all break the model in different directions. The practical reading: effectiveType tells you the browser's recent-average GUESS, useful for choosing between SD and HD, useless for promising an experience. This is why the honest integrations use it as one input among several - saveData, their own timing data, user settings - rather than the arbiter.

What exactly does saveData promise me?

That the user flipped a data-saver switch - Android's Data Saver, a browser-level setting - and asked the platform to spend less on their behalf. It is the only field in the API that represents user intent rather than browser inference, which makes it the only one you can act on without second-guessing: defer prefetch and preconnect, serve compressed or static media, skip autoplay, and batch background sync. The reciprocity rule: users who enable saveData are not asking for a broken experience, they are asking for a lean one - honor it with visible restraint (faster loads, no surprises), not with a stripped page. And it costs nothing to respect in the API's absence: the CSS prefers-reduced-data pattern and your own low-data toggle cover the engines that never shipped navigator.connection.

How fresh are downlink and rtt - can I poll them for a live speed indicator?

They are the wrong primitive for a live indicator. The values update as the browser accumulates recent-connection evidence - seconds behind reality, quantized (rtt in 25ms steps, downlink capped at 10 or 20 Mb/s depending on version), and sticky across idle periods (wake the device and the first readings describe the pre-sleep network). Polling them for a speedometer shows your users a laggy, stair-stepped estimate of the past. If you need live truth, measure it: fetch a known-size resource and time it (respecting cache headers), or read Resource Timing entries your own requests already generate - the API describes the network as the browser last saw it; your own traffic is the network as it is.

What should my app do when navigator.connection is undefined?

What it should do anyway. The undefined branch - Safari, Firefox, every iOS browser, roughly half of real traffic - forces the design you should have shipped everywhere: adaptive defaults (media starts at a sensible quality with an upshift control), lazy loading below the fold regardless of labels, prefetch budgets modest enough to never matter, and server-side compression as the default. Then improve the supported path incrementally: saveData branches, effectiveType-tuned media floors, change-event re-reads. The failure mode to avoid is the inverse - a 'fast lane' the API unlocks that half your users can never enter. The measurement fallback (Navigation and Resource Timing) works on every engine and gives you the population truth the API only sketches.

Related tools