network-guard-engine
v1.0.3
Published
Zero-dependency network status monitor with pluggable handlers.
Readme
network-guard-engine
Zero-dependency network status monitor with pluggable notification handlers.
Works in React, Next.js, and vanilla JavaScript — bring your own notification system or use none at all.
Table of Contents
- Features
- Installation
- Quick Start
- Vanilla JavaScript
- React Integration
- Next.js Integration
- Built-in Handlers
- API Reference
- Browser Support
- TypeScript
Features
- Zero runtime dependencies — core has no dependencies
- SSR-safe — all browser APIs are properly guarded
- Pluggable architecture — use any notification system
- Debounced events — prevents flicker on unstable connections
- Fully typed — built with TypeScript
- Tree-shakeable — framework integrations are isolated entry points
- React 19 compatible — uses ref-stabilized callbacks, no stale closures
- Framework-specific entry points —
react,next,handlers
Installation
npm install network-guard-engine
# or
pnpm add network-guard-engine
# or
yarn add network-guard-engineQuick Start
import { createNetworkMonitor } from "network-guard-engine";
const monitor = createNetworkMonitor({
onOnline: ({ message }) => console.log(message),
onOffline: ({ message }) => console.warn(message),
});
// Stop when done
monitor.stop();Vanilla JavaScript
createNetworkMonitor(options?)
The simplest way to get started. Creates a monitor and starts it immediately.
import { createNetworkMonitor } from "network-guard-engine";
const monitor = createNetworkMonitor({
messages: {
online: "Connection restored ✓",
offline: "Connection lost ✗",
offlineOnMount: "No network connection detected",
},
debounce: 800, // wait 800ms before firing (default: 500)
checkOnMount: true, // fire offline handler immediately if offline (default: true)
onOnline: ({ message, timestamp, isInitial }) => {
console.log(`[${timestamp.toLocaleTimeString()}] ${message}`);
},
onOffline: ({ message, isInitial }) => {
if (!isInitial) console.warn("Lost connection:", message);
},
});
// Clean up when done (e.g. on page unload or SPA route change)
monitor.stop();new NetworkMonitor(options?)
Use the class directly when you need more control, such as starting and stopping imperatively.
import { NetworkMonitor } from "network-guard-engine";
const monitor = new NetworkMonitor({
onOnline: ({ message }) => showToast(message, "success"),
onOffline: ({ message }) => showToast(message, "error"),
});
// Start manually (e.g. after user logs in)
monitor.start();
// Check status at any time
console.log(monitor.running); // true
console.log(monitor.status); // "online" | "offline" | null
// Stop and restart
monitor.stop();
monitor.start();React Integration
# React is a peer dependency — install if you haven't already
npm install reactuseNetworkStatus
The simplest React hook. Returns the current network status as reactive values.
import { useNetworkStatus } from "network-guard-engine/react";
function App() {
const { isOnline, isOffline, status, lastChanged } = useNetworkStatus();
return (
<>
{isOffline && (
<div className="banner">
You are offline
{lastChanged && ` since ${lastChanged.toLocaleTimeString()}`}
</div>
)}
<main>{/* your app */}</main>
</>
);
}Return values:
| Field | Type | Description |
|---------------|-------------------------|------------------------------------------------------|
| status | "online" \| "offline" | Current network status |
| isOnline | boolean | Shorthand for status === "online" |
| isOffline | boolean | Shorthand for status === "offline" |
| lastChanged | Date \| null | Timestamp of the last status change, or null |
useNetworkMonitor
A lower-level hook that provides imperative start / stop control alongside the reactive status values. Useful when you need to conditionally enable monitoring or expose controls in your UI.
import { useNetworkMonitor } from "network-guard-engine/react";
function MonitorPanel() {
const { isOnline, isOffline, isMonitoring, start, stop, lastChanged } =
useNetworkMonitor({
debounce: 800,
messages: { offline: "Connection interrupted." },
});
return (
<div>
<p>Status: {isOnline ? "🟢 Online" : "🔴 Offline"}</p>
{lastChanged && <p>Since: {lastChanged.toLocaleTimeString()}</p>}
<button onClick={isMonitoring ? stop : start}>
{isMonitoring ? "Stop monitoring" : "Start monitoring"}
</button>
</div>
);
}Options:
| Option | Type | Default | Description |
|----------------|-------------------|----------|-------------------------------------------------|
| debounce | number | 500 | Milliseconds to wait before firing a handler |
| checkOnMount | boolean | true | Fire offline handler on mount if already offline|
| messages | NetworkMessages | built-in | Custom message strings |
Return values (extends UseNetworkStatusReturn):
| Field | Type | Description |
|----------------|------------|--------------------------------------------------|
| isMonitoring | boolean | true while the monitor is actively listening |
| start | () => void | Start monitoring (no-op if already running) |
| stop | () => void | Stop monitoring and cancel pending debounce |
NetworkProvider
A provider component that fires your own notification callbacks on network changes. It has no built-in UI — bring your own toast library, Redux action, or Zustand setter.
import { NetworkProvider } from "network-guard-engine/react";
import { toast } from "sonner";
let offlineToastId: string | number | null = null;
export function Providers({ children }: { children: React.ReactNode }) {
return (
<NetworkProvider
messages={{
online: "Back online!",
offline: "Connection lost. Please check your network.",
}}
onOnline={({ message }) => {
if (offlineToastId !== null) toast.dismiss(offlineToastId);
toast.success(message, { duration: 3000 });
offlineToastId = null;
}}
onOffline={({ message }) => {
if (offlineToastId !== null) toast.dismiss(offlineToastId);
offlineToastId = toast.error(message, { duration: Infinity });
}}
>
{children}
</NetworkProvider>
);
}Props:
| Prop | Type | Default | Description |
|----------------|-------------------|----------|----------------------------------------------------------|
| children | React.ReactNode | — | Required. Rendered as-is. |
| onOnline | NetworkHandler | — | Called when connection is restored |
| onOffline | NetworkHandler | — | Called when connection is lost |
| onChange | NetworkHandler | — | Called on both online and offline events |
| debounce | number | 500 | Milliseconds to debounce before firing handlers |
| checkOnMount | boolean | true | Fire onOffline on mount if already offline |
| messages | NetworkMessages | built-in | Custom message strings |
Spread with built-in handlers:
import { consoleHandler } from "network-guard-engine/handlers";
<NetworkProvider {...consoleHandler({ offlineLevel: "error" })}>
{children}
</NetworkProvider>With Zustand:
import { useNetworkStore } from "@/stores/network";
function AppShell({ children }: { children: React.ReactNode }) {
const setOnline = useNetworkStore((s) => s.setOnline);
const setOffline = useNetworkStore((s) => s.setOffline);
return (
<NetworkProvider onOnline={setOnline} onOffline={setOffline}>
{children}
</NetworkProvider>
);
}With onChange (single handler for both events):
<NetworkProvider
onChange={({ status, isInitial, timestamp }) => {
analytics.track("network_status_changed", { status, isInitial, timestamp });
}}
>
{children}
</NetworkProvider>Next.js Integration
The /next entry re-exports everything from /react with a "use client" directive already prepended, so you don't need to add it yourself.
// app/providers.tsx
import { NetworkProvider } from "network-guard-engine/next";
import { toast } from "sonner";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<NetworkProvider
onOnline={({ message }) => toast.success(message)}
onOffline={({ message }) => toast.error(message, { duration: Infinity })}
>
{children}
</NetworkProvider>
);
}// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}Hooks are available from the /next entry too:
"use client";
import { useNetworkStatus } from "network-guard-engine/next";Built-in Handlers
Import from network-guard-engine/handlers (or from the main entry for vanilla usage).
consoleHandler
Logs network status changes to the browser or Node.js console.
import { createNetworkMonitor } from "network-guard-engine";
import { consoleHandler } from "network-guard-engine/handlers";
createNetworkMonitor({
...consoleHandler({
offlineLevel: "error", // "log" | "warn" | "error" (default: "warn")
onlineLevel: "info", // "log" | "info" (default: "log")
showTimestamp: true, // (default: true)
messages: {
online: "Connected",
offline: "Disconnected",
},
}),
});Output example:
[14:32:07] Disconnected ← offline (logged via console.error)
[14:32:45] Connected ← online (logged via console.info)alertHandler
Shows a native window.alert() dialog on each status change.
Best for quick debugging or environments without a notification system. Not recommended for production.
import { createNetworkMonitor } from "network-guard-engine";
import { alertHandler } from "network-guard-engine/handlers";
createNetworkMonitor({
...alertHandler({
messages: {
online: "You're back online.",
offline: "You lost your connection.",
},
}),
});customHandler
The recommended adapter for integrating any external toast or notification library. Receives a clean message string plus the full NetworkEventPayload.
import { createNetworkMonitor } from "network-guard-engine";
import { customHandler } from "network-guard-engine/handlers";
import { toast } from "sonner";
let offlineToastId: string | number | null = null;
createNetworkMonitor({
...customHandler({
messages: {
online: "Back online!",
offline: "Connection lost. Please check your network.",
offlineOnMount: "No internet connection detected.",
},
onOnline: (message) => {
if (offlineToastId !== null) toast.dismiss(offlineToastId);
toast.success(message, { duration: 3000 });
offlineToastId = null;
},
onOffline: (message) => {
if (offlineToastId !== null) toast.dismiss(offlineToastId);
offlineToastId = toast.error(message, { duration: Infinity });
},
}),
});Full payload access (second argument):
customHandler({
onOffline: (message, payload) => {
// payload.isInitial — true only on the first mount check
if (!payload.isInitial) Sentry.captureMessage("User went offline");
showBanner(message);
},
});With React Hot Toast:
import toast from "react-hot-toast";
createNetworkMonitor({
...customHandler({
onOnline: (message) => toast.success(message),
onOffline: (message) => toast.error(message, { duration: Infinity }),
}),
});domBannerHandler
Injects a fixed, animated banner at the top of the page — no React, no external libraries required.
- Created lazily on the first event and reused for subsequent events.
- Slides in/out using CSS transitions.
- Fully accessible: sets
role="alert"andaria-live="assertive".
import { createNetworkMonitor } from "network-guard-engine";
import { domBannerHandler } from "network-guard-engine/handlers";
createNetworkMonitor({
...domBannerHandler(),
});With custom options:
createNetworkMonitor({
...domBannerHandler({
container: "#app-root", // CSS selector or HTMLElement (default: document.body)
onlineDuration: 2500, // ms before hiding the "back online" banner (default: 3000)
messages: {
online: "Reconnected!",
offline: "No connection",
offlineOnMount: "You appear to be offline.",
},
offlineStyle: {
background: "#7f1d1d",
letterSpacing: "0.05em",
},
onlineStyle: {
background: "#14532d",
},
}),
});Options:
| Option | Type | Default | Description |
|------------------|----------------------------------|-----------------|-------------------------------------------------------|
| messages | NetworkMessages | built-in | Custom text for each status |
| container | string \| HTMLElement | document.body | Where to mount the banner element |
| offlineStyle | Partial<CSSStyleDeclaration> | — | Additional styles applied while offline |
| onlineStyle | Partial<CSSStyleDeclaration> | — | Additional styles applied while online |
| onlineDuration | number | 3000 | How long (ms) the "back online" banner stays visible. Set 0 to keep it until next change. |
API Reference
NetworkEventPayload
Delivered to every handler. All fields are readonly — the object is frozen.
type NetworkEventPayload = {
readonly status: "online" | "offline";
readonly message: string; // resolved human-readable message
readonly isInitial: boolean; // true only on the first mount check
readonly timestamp: Date; // when this event was fired
};NetworkMessages
All fields are optional and fall back to built-in defaults.
type NetworkMessages = {
online?: string; // default: "Back online"
offline?: string; // default: "You are offline. Please check your connection."
offlineOnMount?: string; // default: "No internet connection. Please check your network."
};NetworkMonitor class
new NetworkMonitor(options?: NetworkMonitorOptions): NetworkMonitor
monitor.start(): this // start listening, returns this (chainable)
monitor.stop(): this // stop and cancel pending debounce, returns this
monitor.running: boolean // true while actively listening
monitor.status: "online" | "offline" | null // last known status (null before first start)createNetworkMonitor(options?)
createNetworkMonitor(options?: NetworkMonitorOptions): NetworkMonitor
// Equivalent to: new NetworkMonitor(options).start()NetworkMonitorOptions
| Option | Type | Default | Description |
|----------------|-------------------|----------|---------------------------------------------------------|
| debounce | number | 500 | Milliseconds to wait before firing after a status change|
| checkOnMount | boolean | true | Fire onOffline immediately on start if already offline|
| onOnline | NetworkHandler | — | Called when connection is restored |
| onOffline | NetworkHandler | — | Called when connection is lost |
| messages | NetworkMessages | built-in | Custom message strings |
Browser Support
| Browser | Support | |---------------|---------| | Chrome 66+ | ✅ | | Firefox 60+ | ✅ | | Safari 12.1+ | ✅ | | Edge 79+ | ✅ | | Node.js / SSR | ✅ (safe — all browser APIs are guarded) |
The library uses window.addEventListener("online" / "offline") and navigator.onLine, which have broad browser support. The core module is SSR-safe: every window and navigator access is guarded, so you can safely import and instantiate the monitor in a server environment — it simply becomes a no-op until .start() is called in the browser.
TypeScript
All types are exported from the main entry point:
import type {
NetworkStatus,
NetworkMessages,
NetworkEventPayload,
NetworkHandler,
NetworkMonitorOptions,
NetworkProviderProps,
UseNetworkStatusReturn,
UseNetworkMonitorReturn,
AlertHandlerOptions,
ConsoleHandlerOptions,
CustomHandlerOptions,
DomBannerHandlerOptions,
} from "network-guard-engine";