useOnlineStatus Hook for React
A typed React hook that tracks the browser online/offline state and re-renders components when the network status changes.
useOnlineStatus is a React hook that returns true when the browser has a network connection and false when it doesn't. It listens to the online and offline events on window and re-renders consumers automatically. Use it to show offline banners, queue API calls, or disable submit buttons during disconnects.
Tested on React 19, modern browsers.
When to Use This
- Showing an "offline" banner when the user loses connection
- Disabling form submit buttons during network outages
- Switching to a cached/local data source when offline
- Triggering retry logic when connection comes back
Don't use this when you need ground-truth API reachability (use a real heartbeat to your server) or when the entire app is offline-first (use a service worker with proper sync).
Code
import { useEffect, useState } from 'react';
export function useOnlineStatus(): boolean {
// SSR-safe default: assume online so we don't flash an offline banner
const [online, setOnline] = useState<boolean>(() =>
typeof navigator === 'undefined' ? true : navigator.onLine
);
useEffect(() => {
const handleOnline = () => setOnline(true);
const handleOffline = () => setOnline(false);
// Sync with current state in case it changed before listeners attached
setOnline(navigator.onLine);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return online;
}The lazy initializer in useState checks for navigator so it works during server rendering. The setOnline(navigator.onLine) inside the effect re-syncs in case the user went offline between mount and effect run.
Usage
An offline banner that appears at the top of the app:
'use client';
import { useOnlineStatus } from '@/hooks/useOnlineStatus';
import { WifiOff } from 'lucide-react';
export function OfflineBanner() {
const online = useOnlineStatus();
if (online) return null;
return (
<div className="fixed top-0 inset-x-0 bg-amber-500 text-white px-4 py-2 flex items-center gap-2 z-50">
<WifiOff className="h-4 w-4" />
<span>You are offline. Some features may not work.</span>
</div>
);
}The banner only renders when offline, which avoids any visual cost while online.
Things to Know
navigator.onLineis famously unreliable. It only knows whether the browser has a network interface, not whether your servers are reachable. A captive portal looks "online" to the browser.- Don't use this as your only retry trigger. Combine it with actual fetch error handling. Some failures (DNS, CORS, server down) happen while online.
- The
onlineandofflineevents fire only on transitions. If the user starts the page offline, the events won't fire — that's why we re-sync inside the effect. - Mobile browsers throttle background tabs. A backgrounded tab may not receive offline events until it's foregrounded again.
- Don't use this for connectivity-aware features that need precise timing. Use the Network Information API for downlink speed and effective connection type.
Related Snippets & Reading
- useDebounce Hook for React — combine for retry-after-pause patterns
- useCopyToClipboard Hook — another small browser-API wrapper
- MDN navigator.onLine docs — caveats and edge cases
Frequently Asked Questions
How accurate is navigator.onLine?
navigator.onLine reports whether the browser thinks it has a network connection, but it doesn't verify reachability to your actual server. A user could be on Wi-Fi (online: true) but unable to reach your API. Treat it as a hint, not a guarantee.
Does useOnlineStatus work with SSR?
Yes, but the initial render uses a sensible default (true) because navigator is undefined on the server. The real value updates on the first client effect. This avoids hydration mismatches.