npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@connectivity-kit/svelte

v0.1.3

Published

Minimal, SSR-safe browser connectivity tracking (online + real reachability) for Svelte 5.

Readme

@connectivity-kit/svelte

Minimal, SSR-safe connectivity tracking for Svelte 5. Tracks two distinct facts and combines them:

  • online — the OS/browser network interface is up (navigator.onLine plus the online/offline window events)
  • reachable — the last reachability probe actually succeeded — real internet access, not just an active interface. Connected to wifi with no upstream is online: true, reachable: false.

Browser-only by design: no adapter interface, no pluggable transport abstraction, no native-platform portability layer. There's exactly one source of truth (the browser), so there's nothing to normalize across.

Install

npm install @connectivity-kit/svelte

svelte (^5.0.0) is a peer dependency — this package doesn't bundle its own copy.

Quick start

<script lang="ts">
	import { ConnectivityManager } from '@connectivity-kit/svelte';

	const connectivity = new ConnectivityManager();

	// The manager never ties its own lifecycle to a component automatically
	// — tear it down explicitly on unmount.
	$effect(() => () => connectivity.destroy());
</script>

{#if connectivity.isConnected}
	<span class="badge badge-success">Online</span>
{:else if connectivity.online}
	<span class="badge badge-warning">Limited connectivity</span>
{:else}
	<span class="badge badge-error">Offline</span>
{/if}

If you only need one instance for the whole app, construct it once in a shared module instead of per-component and skip the destroy() call.

API

class ConnectivityManager {
	constructor(options?: ConnectivityManagerOptions);

	get online(): boolean;      // network interface is up
	get reachable(): boolean;   // last probe succeeded — real internet access
	get isConnected(): boolean; // online && reachable
	get checking(): boolean;    // a probe is currently in flight

	readonly ready: Promise<void>; // resolves after the first probe settles

	check: ConnectivityCheck;   // an arrow-function field — see "Injecting check()" below
	destroy(): void;            // release all listeners/timers — idempotent
}

interface ConnectivityManagerOptions {
	reachabilityUrl?: string | null;      // default '/favicon.ico'; null disables probing
	reachabilityIntervalMs?: number;      // default 30_000
	reachabilityTimeoutMs?: number;       // default 5_000
	checkCooldownMs?: number;             // default 1_000 — see "check()" below
	isBrowser?: boolean;                  // override environment detection (mainly for tests)
}

interface ConnectivityCheckOptions {
	force?: boolean; // bypass the cooldown for this call
}

interface ConnectivityStatus {
	readonly online: boolean;
	readonly reachable: boolean;
	readonly isConnected: boolean;
	readonly checkedAt: string; // ISO-8601, of the last *completed* probe
}

type ConnectivityCheck = (options?: ConnectivityCheckOptions) => Promise<ConnectivityStatus>;

All exported types are prefixed Connectivity* deliberately — this is a published package, and generic names like StatusResult or CheckOptions are exactly the kind of thing likely to collide with another library's (or your own app's) exports in a consumer's autocomplete.

Injecting check()

check is defined as an arrow-function class field, not a prototype method — it stays correctly bound to its instance even when detached and passed around on its own:

const { check } = connectivity;
await check(); // works — no .bind(connectivity) needed

function RetryButton({ check }: { check: ConnectivityCheck }) {
	// ...
}

A plain method wouldn't survive this (calling it detached would throw, since it reads private instance fields internally) — the same reason the manager's internal online/offline event handlers are also arrow-function fields rather than methods.

check(): cooldown and in-flight coalescing

Calling check() repeatedly in a short window (a user mashing "Retry", or two components each calling it on mount) is handled two ways:

  • In-flight coalescing. If a probe is already running — from check(), the online event, or the background interval — a new check() call joins that same probe instead of starting a second fetch. This isn't configurable; overlapping probes are never useful.
  • Post-completion cooldown. After a probe finishes, a non-force check() call within checkCooldownMs (default 1000ms) returns the last completed ConnectivityStatus immediately, with no new request. Pass { force: true } to bypass this for a deliberate action, like a real "pull to refresh" gesture.

The cooldown only applies to check() — the automatic online-event and background-interval probes are never throttled by it.

Usage scenarios

Waiting for the first check before rendering anything

<script lang="ts">
	import { ConnectivityManager } from '@connectivity-kit/svelte';

	const connectivity = new ConnectivityManager();
	$effect(() => () => connectivity.destroy());
</script>

{#await connectivity.ready}
	<Spinner />
{:then}
	{#if connectivity.isConnected}
		<slot />
	{:else}
		<OfflineBanner />
	{/if}
{/await}

A "Retry" button

<script lang="ts">
	import { ConnectivityManager } from '@connectivity-kit/svelte';
	const connectivity = new ConnectivityManager();
	$effect(() => () => connectivity.destroy());
</script>

{#if !connectivity.isConnected}
	<button
		onclick={() => connectivity.check({ force: true })}
		disabled={connectivity.checking}
	>
		{connectivity.checking ? 'Checking…' : 'Retry'}
	</button>
{/if}

force: true here is deliberate: a user pressing "Retry" expects it to actually retry, not silently no-op because it happened to land inside the default cooldown window.

Reading a snapshot outside a component

const result = await connectivity.check();
// result: { online, reachable, isConnected, checkedAt }
sendAnalyticsEvent('connectivity_checked', result);

Custom reachability target

const connectivity = new ConnectivityManager({
	reachabilityUrl: '/api/health',
	reachabilityIntervalMs: 15_000
});

Disabling the reachability probe entirely

const connectivity = new ConnectivityManager({ reachabilityUrl: null });

connectivity.reachable will stay false and connectivity.isConnected will always equal connectivity.online in this mode.

One instance monitoring two targets

const apiHealth = new ConnectivityManager({ reachabilityUrl: '/api/health' });
const paymentsHealth = new ConnectivityManager({ reachabilityUrl: 'https://status.payment-provider.com/ping' });

Testing with isBrowser

import { describe, it, expect } from 'vitest';
import { ConnectivityManager } from '@connectivity-kit/svelte';

it('reports offline outside a browser context', async () => {
	const manager = new ConnectivityManager({ isBrowser: false });
	await manager.ready;
	expect(manager.online).toBe(false);
	manager.destroy();
});

SSR safety

Outside a browser context (typeof window === 'undefined'), the manager never touches window/navigator, never calls fetch, and never schedules the background probe timer — all of which would otherwise be permanently leaked on the server, since nothing calls destroy() on a server-rendered instance. It reports online: false / reachable: false and resolves ready immediately.

This safety net is keyed off the resolved isBrowser option, not a fresh environment check on every use — so passing isBrowser: true explicitly on the server (there's no legitimate reason to) reintroduces that leak. Only pass isBrowser in tests that need to simulate one environment or the other.

Reachability semantics

Any HTTP response — including a 404 — counts as reachable; only a network-level failure (including our own timeout) counts as unreachable. This means reachabilityUrl doesn't need to be a dedicated health-check endpoint; most apps already serve something at their default /favicon.ico.