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

react-native-vpn-listener

v1.0.0

Published

A React Native library that provides a unified, cross-platform way to detect VPN connectivity on iOS and Android using the New Architecture (TurboModules with codegen).

Readme

react-native-vpn-listener

Reliable, event‑driven VPN detection for React Native — on both platforms. Know instantly when your users connect or disconnect a VPN, with connection details, not just a boolean.

npm license platforms

Built for apps where VPN state actually matters:

  • 🎬 Streaming / media — geo‑licensing compliance
  • 💳 Fintech & payments — fraud signals, risk scoring
  • 🛒 E‑commerce & ticketing — regional pricing & catalog abuse prevention
  • 🏢 Security & MDM — enforce or verify VPN posture

✨ Features

  • 🔔 Truly event‑driven on both platforms (NWPathMonitor on iOS, ConnectivityManager callbacks on Android — no polling, no battery drain)
  • 🪝 useVpnStatus() React hook for one‑line integration
  • 📱 One JS API, both platforms — including iOS, where most alternatives fall short
  • 🔍 Details, not just a boolean: interface, addresses, DNS (best‑effort, honestly typed per platform)
  • ⚡ New Architecture TurboModule (fast, typed, codegen‑driven)
  • 🛡 Public APIs only — no private API risk for App Store review

🥊 Comparison

| | react-native-vpn-listener | @react-native-community/netinfo | react-native-vpn-detector | react-native-vpn-status | | ----------------------------------- | ------------------------- | ------------------------------- | ------------------------- | ----------------------- | | VPN detection on Android | ✅ | ✅ | ✅ | ✅ | | VPN detection on iOS | ✅ | ❌ | ✅ | ✅ | | Live change events | ✅ event‑driven | ✅ | ❌ one‑shot check | ✅ | | Connection details (interface, IPs) | ✅ | ❌ | ❌ boolean only | ❌ | | React hook | ✅ useVpnStatus() | ✅ | ❌ | ❌ | | New Architecture (TurboModule) | ✅ native | ➖ interop | ❌ old architecture | ❌ old architecture |

✅ Requirements

| Runtime | Minimum | | ------------ | ----------------------------------------------- | | React Native | 0.80+ (New Architecture required; tested on 0.86) | | iOS | 13.4+ | | Android | minSdk 24+ | | Expo | Dev Client / EAS builds (not Expo Go) |

📦 Installation

npm install react-native-vpn-listener
# or
yarn add react-native-vpn-listener

iOS

cd ios && pod install

Expo

  • Supported via Expo Dev Client and EAS builds.
  • Use npx expo run:ios / npx expo run:android for development builds.

🚀 Quick Start

import { Text, View } from 'react-native';
import { useVpnStatus } from 'react-native-vpn-listener';

export default function App() {
  const vpn = useVpnStatus(); // null until the first snapshot arrives

  return (
    <View>
      <Text>Active: {vpn?.active ? 'yes' : 'no'}</Text>
      <Text>Type: {vpn?.type}</Text>
      <Text>Interface: {vpn?.interfaceName ?? '-'}</Text>
      <Text>Local IP: {vpn?.localAddress ?? '-'}</Text>
    </View>
  );
}

Prefer imperative APIs? isVpnActive(), getVpnInfo(), and onChange() are also exported:

import { isVpnActive, getVpnInfo, onChange } from 'react-native-vpn-listener';

const active = await isVpnActive();
const info = await getVpnInfo();
const sub = onChange((next) => console.log('VPN changed:', next));
// later: sub.remove();

📖 API Reference

Methods

  • useVpnStatus(): VpnInfo | null
    • React hook returning the current VPN status; re‑renders on every change. null until the first snapshot arrives.
  • isVpnActive(): Promise<boolean>
    • Returns whether a VPN‑like interface is currently active.
  • getVpnInfo(): Promise<VpnInfo>
    • Returns a snapshot with details (see Types).
  • onChange(cb: (info: VpnInfo) => void): { remove(): void }
    • Subscribes to status changes; call remove() to unsubscribe.

Events (semantics)

  • Android: fires on ConnectivityManager network callbacks (VPN networks and default‑network changes), de‑duplicated so unrelated Wi‑Fi↔cellular transitions don't emit.
  • iOS: fires on NWPathMonitor path updates (no polling) and de‑noised (emits only when fields other than timestamp change). Sends one initial snapshot after subscription.

🧾 Types

export type VpnType =
  | 'none'
  | 'ipsec'
  | 'ikev2'
  | 'openvpn'
  | 'wireguard'
  | 'l2tp'
  | 'pptp'
  | 'unknown';

export type VpnInfo = {
  active: boolean;
  type: VpnType; // heuristic (see notes)
  interfaceName: string | null; // e.g., utun4 (iOS), tun0 (Android); null when inactive
  localAddress: string | null; // local tunnel IP; null when inactive
  remoteAddress: string | null; // Android best‑effort; always null on iOS
  dns: string[]; // Android best‑effort; always empty on iOS
  timestamp: number; // ms since epoch
  platform: 'ios' | 'android';
};

Notes:

  • iOS never populates dns or remoteAddress (public APIs do not expose them).
  • On iOS, type is almost always 'unknown' when active — every VPN appears as a generic utun interface.
  • Android may omit fields depending on device/OS.

🔧 Configuration

iOS

  • No Info.plist changes or special entitlements.
  • Uses getifaddrs to enumerate interfaces.

Android

  • Requires ACCESS_NETWORK_STATE. Declared by the library and merged automatically.

🧭 Platform Support

| Platform | Status | Notes | | -------- | ------ | --------------------------------------------------------------------- | | iOS | ✅ | NWPathMonitor event‑driven; public APIs only; simulator forced inactive | | Android | ✅ | ConnectivityManager callbacks; best‑effort details |

⚠️ Limitations

  • iOS detection is heuristic: a VPN‑ish interface (utun/ppp/ipsec) must be UP+RUNNING with a private/CGNAT IPv4 or a routable (non‑link‑local) IPv6 address. IPv6‑only VPNs are detected; system services that route traffic through a utun with a routable address (e.g. some iCloud Private Relay configurations) may register as a VPN.
  • iOS Simulator: always reported as inactive (simulator utun can resemble VPN).
  • Android details (DNS, gateway) are best‑effort and may vary by OEM/OS.

🧪 Example App

A runnable example is included under example/.

cd example
yarn
yarn start

🧰 Development

git clone https://github.com/hamzamekk/react-native-vpn-listener.git
cd react-native-vpn-listener
yarn
cd example
yarn
yarn android
# or
yarn ios

Please include minimal repro steps for native changes. TypeScript and modern RN patterns preferred.

🛠 Troubleshooting

  • iOS events not firing:
    • cd ios && pod install, then Clean Build Folder in Xcode and rerun
    • Subscribe via onChange before fetching
    • Toggle Wi‑Fi/VPN to trigger updates
  • Android build issues:
    • Use JDK 17; install Android SDK; ensure ANDROID_HOME is set

📄 License

MIT