net-watch
Reference

State reference

The NetworkState object, every field, which browsers provide it, and the exported types.

watcher.getState() returns a NetworkState, and subscribe passes one to every listener. The framework adapters return the same object.

interface NetworkState {
  online: boolean
  since: number
  quality: Quality
  rtt: number | undefined
  downlink: number | undefined
  effectiveType: EffectiveType | undefined
  type: ConnectionType | undefined
  saveData: boolean
}

Fields

FieldTypeMeaningAvailable in
onlinebooleanThe user can really reach the network. See Detecting offlineEvery browser
sincenumberDate.now() of the last online/offline flip. At creation, the time the watcher was createdEvery browser
qualityQualityBand over the smoothed rtt. 'unknown' until a sample exists, and while offlineEvery browser, once a sample exists
rttnumber | undefinedSmoothed round-trip time in milliseconds, rounded to 50Every browser, once a sample exists. Chromium also provides one at startup
downlinknumber | undefinedSmoothed throughput in Mbps. Rounded to 0.1 below 10, to whole Mbps from 10Chrome and Edge, Firefox, Safari 16.4 and later
effectiveTypeEffectiveType | undefinedThe browser's own speed classChrome and Edge only
typeConnectionType | undefinedThe kind of link, for example 'wifi' or 'cellular'Chrome on Android and ChromeOS only
saveDatabooleanThe user turned on a data saverChrome and Edge. false everywhere else

How rtt, quality and downlink are computed is in Network quality. The browser matrix is in Browser support.

Example

import { createNetworkWatcher } from '@net-watch/core'

const watcher = createNetworkWatcher({ pingUrl: '/api/ping' })

// Right after creation nothing has been measured: quality is 'unknown' and rtt is undefined
// (Chromium may already fill them in from navigator.connection).

watcher.subscribe((state) => {
  console.log(state.online) // true
  console.log(state.quality) // 'good'
  console.log(state.rtt) // 150
  console.log(state.effectiveType) // '4g' in Chrome, undefined in Firefox and Safari
})

Always handle undefined:

const label = state.rtt === undefined ? 'measuring...' : `${state.rtt} ms`

State rules

  • Immutable. A change replaces the state object. No change keeps the same reference. Compare states with === and treat them as read-only.
  • Notify on visible change only. Listeners fire when any field changes after rounding. rtt is rounded to 50 ms and downlink to 0.1 Mbps (whole Mbps from 10), so a stream of similar samples causes no re-renders. since does not trigger a notification on its own. It changes together with online.
  • Unknown is undefined, never a guess. A browser that does not expose a value leaves it undefined. quality uses 'unknown' and saveData uses false for the same case.
  • Order. onOnline and onOffline run first, then listeners in subscription order.
  • Going offline sets quality to 'unknown' and clears rtt and downlink.
  • SSR. Without window, the state is online: true, quality: 'unknown', every metric undefined and saveData: false. No listeners or timers are created. See the SSR guide.

Exported types

All of these are exported from @net-watch/core.

Quality

type Quality = 'excellent' | 'good' | 'fair' | 'poor' | 'unknown'

EffectiveType

type EffectiveType = 'slow-2g' | '2g' | '3g' | '4g'

ConnectionType

type ConnectionType =
  | 'bluetooth'
  | 'cellular'
  | 'ethernet'
  | 'wifi'
  | 'wimax'
  | 'none'
  | 'other'
  | 'unknown'

RttBounds

type RttBounds = readonly [excellent: number, good: number, fair: number]

Upper bounds in milliseconds of the excellent, good and fair bands, in ascending order. Anything slower is poor. The default is [150, 400, 1400].

NetworkListener

type NetworkListener = (state: NetworkState, prev: NetworkState) => void

The callback type for subscribe.

NetworkWatcher

interface NetworkWatcher {
  getState: () => NetworkState
  subscribe: (listener: NetworkListener) => () => void
  check: () => Promise<NetworkState>
  report: (ok: boolean, rtt?: number) => void
  waitForOnline: () => Promise<NetworkState>
  destroy: () => void
}

Each method is documented in the API reference.

WatcherOptions

interface WatcherOptions {
  pingUrl?: string
  timeout?: number
  maxRetryDelay?: number
  heartbeat?: number
  rttBounds?: RttBounds
  onOnline?: (state: NetworkState) => void
  onOffline?: (state: NetworkState) => void
}

Defaults and descriptions are in the options table.

Using the types

import type { NetworkState, Quality } from '@net-watch/core'

const rank: Record<Exclude<Quality, 'unknown'>, number> = {
  excellent: 3,
  good: 2,
  fair: 1,
  poor: 0,
}

export function canUploadVideo(state: NetworkState): boolean {
  return state.online && state.quality !== 'unknown' && rank[state.quality] >= 2
}

On this page