net-watch

Quick start

Install net-watch, watch the connection and show an offline banner in a few minutes.

Install

npm i @net-watch/core

For a framework adapter, install it next to core: @net-watch/react, @net-watch/vue or @net-watch/angular.

Create a watcher

Create one watcher for the whole app and keep it in a module.

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

export const watcher = createNetworkWatcher({
  pingUrl: '/api/ping', // optional: lets net-watch confirm the user can really reach your server
})

Without pingUrl, net-watch still reacts when the browser reports that the network is gone, and it treats a successful request as proof that you are online. It cannot confirm a failed request, so "connected to Wi-Fi but no internet" goes unnoticed. The ping endpoint guide shows how to add one in a few lines.

Read the state

const { online, quality, rtt } = watcher.getState()

const stop = watcher.subscribe((state, previous) => {
  if (!state.online) console.log('offline since', new Date(state.since))
})
// later: stop()

getState() returns the same object until something changes, so it is safe to use with useSyncExternalStore and signals.

Measure with your real requests

Wrap fetch once. Every response becomes a free latency sample, and a network error makes net-watch check the connection.

import { watchFetch } from '@net-watch/core/fetch'
import { watcher } from './network'

export const api = watchFetch(watcher)

const res = await api('/api/projects') // use it like fetch

Show a banner

import { createBanner } from '@net-watch/core/banner'
import { watcher } from './network'

createBanner(watcher, { slowText: 'Slow connection' })

The banner appears when the user goes offline, shows a message while the connection is poor, and, after a loss of connection, says "Back online" for a few seconds. See the banner guide for text, position and theming.

In your framework

npm i @net-watch/core @net-watch/react
Status.tsx
import { useNetworkStatus } from '@net-watch/react'
import { watcher } from './network'

export function Status() {
  const { online, quality, rtt } = useNetworkStatus(watcher)
  return <p>{online ? `Online, ${quality} (${rtt ?? '?'} ms)` : 'Offline'}</p>
}

The adapters default to a shared watcher without pingUrl. Pass your own watcher, as above, to configure it.

What next

On this page