react-native-wireguard-vpn
v1.0.22
Published
React Native WireGuard VPN package for iOS and Android
Maintainers
Readme
react-native-wireguard-vpn
A React Native module for implementing WireGuard VPN functionality in iOS and Android applications. This package provides a native implementation of WireGuard tunneling with a simple JavaScript interface.
Requirements
- React Native >= 0.72.0
- iOS 12.0 or later
- Android API level 21 (Android 5.0) or later
- CocoaPods for iOS development
- Not compatible with Expo Go — use a development build or bare workflow (e.g.
npx expo run:iosafter prebuild).
Installation
# Using npm
npm install react-native-wireguard-vpn --save
# Using yarn
yarn add react-native-wireguard-vpnExpo Config Plugin (iOS + Android)
If you use Expo (prebuild / dev builds), you can enable the library’s config plugin to automatically add the required Network Extension iOS entitlements + Info.plist keys.
- Add the plugin to your
app.json/app.config.js:
{
"expo": {
"plugins": ["react-native-wireguard-vpn"]
}
}- Re-run prebuild and rebuild your iOS app (and Android dev build if needed):
npx expo prebuild -p ios --clean
npx expo run:ios --deviceNotes:
- This plugin updates iOS Info.plist + entitlements.
- It also ensures required Android VPN permissions are present in
AndroidManifest.xml. - The Packet Tunnel extension target still needs to be created in Xcode manually (File → New Target → Network Extension → Packet Tunnel Provider).
- The library’s native iOS code uses the tunnel provider bundle identifier
com.wireguardvpn.tunnelby default. Make sure your Packet Tunnel extension uses the same Bundle Identifier (or update the native code and rebuild). - The Expo config plugin does not run in Expo Go. Use a development build /
prebuild. - Test on a real device (VPN / Network Extension usually does not work in the simulator).
iOS Setup
- Install CocoaPods dependencies (required for the native module to link):
cd ios && pod install && cd ..Or from project root: npx pod-install (if you use pod-install).
After adding the package, you must run pod install and rebuild the app (e.g. npx expo run:ios or build from Xcode).
- Add the following entries to your Info.plist:
<key>NSLocalNetworkUsageDescription</key>
<string>This app requires access to network features for VPN functionality</string>
<key>UIBackgroundModes</key>
<array>
<string>network-authentication</string>
<string>network</string>
</array>- Add a Packet Tunnel Network Extension to your iOS project:
- In Xcode: File → New → Target → Network Extension → Packet Tunnel Provider. Name it e.g. "WireGuardTunnel".
- Set the extension target’s Bundle Identifier to
com.wireguardvpn.tunnel(or the same value your app uses when starting the tunnel). The library usescom.wireguardvpn.tunnelby default in the native code. - In your main app target: Signing & Capabilities → "+" → add Network Extensions and enable Packet Tunnel.
- Use a real device for testing; VPN/Network Extension often does not work in the simulator.
Android Setup
- Add the following permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.BIND_VPN_SERVICE" />- Update your app's build.gradle to ensure compatibility:
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 21
targetSdkVersion 34
}
}Usage
import WireGuardVpnModule, {
WireGuardConfig,
WireGuardStatus
} from 'react-native-wireguard-vpn';
// Configuration object (e.g. Mullvad-style). Use "address" for your tunnel IP if your provider gives one.
const config: WireGuardConfig = {
privateKey: 'YOUR_PRIVATE_KEY',
publicKey: 'SERVER_PUBLIC_KEY',
serverAddress: 'SERVER_IP',
serverPort: 51820,
address: '10.64.0.1/32', // optional; interface tunnel IP (defaults to 10.64.0.1/32). Use allowedIPs only for routing.
allowedIPs: ['0.0.0.0/0', '::/0'], // use ::/0 for IPv6 default (not ::0/0)
dns: ['1.1.1.1'],
mtu: 1450,
presharedKey: 'OPTIONAL_PRESHARED_KEY' // Optional
};
// Initialize VPN service
await WireGuardVpnModule.initialize();
// Connect to VPN
try {
await WireGuardVpnModule.connect(config);
console.log('Connected successfully');
} catch (error) {
console.error('Connection failed:', error);
}
// Check VPN status
const status: WireGuardStatus = await WireGuardVpnModule.getStatus();
console.log('VPN Status:', status);
// Disconnect from VPN
await WireGuardVpnModule.disconnect();API Reference
Methods
initialize(): Promise<void>
Initializes the VPN service. Must be called before any other operations.
connect(config: WireGuardConfig): Promise<void>
Connects to the VPN using the provided configuration.
disconnect(): Promise<void>
Disconnects from the VPN.
getStatus(): Promise<WireGuardStatus>
Gets the current VPN connection status.
isSupported(): Promise<boolean>
Checks if WireGuard VPN is supported on the device.
Events
vpnStateChanged (iOS + Android)
This event is emitted when the VPN/tunnel state changes.
Payload:
isConnected: booleantunnelState: 'ACTIVE' | 'INACTIVE' | 'CONNECTING' | 'DISCONNECTING' | 'ERROR' | 'UNKNOWN'status: 'CONNECTED' | 'DISCONNECTED' | 'CONNECTING' | 'DISCONNECTING' | 'ERROR' | 'UNKNOWN'
Example:
import { NativeEventEmitter, NativeModules } from 'react-native';
const emitter = new NativeEventEmitter(NativeModules.WireGuardVpnModule);
const sub = emitter.addListener('vpnStateChanged', (payload) => {
console.log('vpnStateChanged', payload);
});
// sub.remove();Types
interface WireGuardConfig {
privateKey: string;
publicKey: string;
serverAddress: string;
serverPort: number;
address?: string | string[]; // optional; tunnel IP e.g. "10.64.0.1/32" (defaults to 10.64.0.1/32)
allowedIPs: string[]; // routing only; use ::/0 for IPv6 default
dns?: string[];
mtu?: number;
presharedKey?: string;
}
interface WireGuardStatus {
isConnected: boolean;
tunnelState:
| 'ACTIVE'
| 'INACTIVE'
| 'CONNECTING'
| 'DISCONNECTING'
| 'ERROR'
| 'UNKNOWN';
status:
| 'CONNECTED'
| 'DISCONNECTED'
| 'CONNECTING'
| 'DISCONNECTING'
| 'ERROR'
| 'UNKNOWN';
error?: string;
}Linking checklist (if you see "doesn't seem to be linked")
- Run
pod installin theiosfolder (ornpx pod-installfrom the project root). - Rebuild the app after installing the package (e.g.
npx expo run:ios --deviceor build from Xcode). A reload is not enough. - Do not use Expo Go. Use a development build (e.g.
npx expo run:iosor a custom dev client). This library uses native code and is not available in Expo Go. - Expo (prebuild): After
npx expo prebuild, runpod installinios/and thennpx expo run:ios.
Troubleshooting
"The package 'react-native-wireguard-vpn' doesn't seem to be linked"
Follow the Linking checklist above. Ensure you have run pod install, rebuilt the app (not just reloaded), and are not using Expo Go.
"Failed to set tunnel state: Bad address" / BackendException (Android)
BackendException from GoBackend.setState() (or "Bad address") usually means the interface address is invalid. The config must use:
address(optional): your tunnel IP in CIDR form, e.g."10.64.0.1/32". If your provider gives an "Address" in the[Interface]section, use that. If omitted, the library uses10.64.0.1/32.allowedIPs: only for routing (what traffic goes through the VPN), e.g.["0.0.0.0/0", "::/0"]. Use::/0for IPv6 default, not::0/0. Do not use0.0.0.0/0or::/0as the interface address.
From v1.0.16+, the library uses a proper interface address by default and surfaces the backend cause in the error message when possible.
Android
- Ensure your app has the necessary permissions granted
- Check logcat for detailed error messages
- Make sure the VPN service is properly declared in AndroidManifest.xml
iOS
- Verify Network Extension capability is properly configured
- Check Xcode logs for detailed error messages
- Ensure proper signing and provisioning profiles are set up
NEConfigurationErrorDomain Code=10 — "permission denied" (real device)
This comes from iOS / Apple Developer, not from JavaScript. Common fixes:
Paid Apple Developer Program — Packet Tunnel VPN usually needs an enrolled paid account ($99/year). A free “Personal Team” often cannot provision Network Extension entitlements correctly on device.
Developer Portal — App IDs — For both the main app and the Packet Tunnel extension target:
- Register each Bundle ID at developer.apple.com.
- Enable Network Extensions (and Packet Tunnel as needed) on each App ID that participates in the VPN.
Provisioning profiles — After changing capabilities, regenerate Development/Distribution profiles, download them (or let Xcode refresh), then clean build (Product → Clean Build Folder) and reinstall on the device.
Extension Bundle ID must match the library — The native code uses
providerBundleIdentifiercom.wireguardvpn.tunnel. Your extension target’s Bundle Identifier must be exactly that, or you must changeWireGuardVpn.m(and rebuild the pod) to match your extension ID (e.g.com.yourcompany.yourapp.tunnel).Capabilities in Xcode — For WireGuard-style tunnels you need Network Extensions → Packet Tunnel. Personal VPN is a different API; you can try removing Personal VPN if you only use a custom Packet Tunnel, to avoid entitlement mismatches. App Proxy is not required for Packet Tunnel only — enable only what your provisioning profile actually includes.
Team & signing — Main app and extension must use the same team, correct Signing Certificate, and profiles that include Network Extension entitlements.
Example App
Check out the example app in our GitHub repository for a complete implementation.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
Support
For bugs and feature requests, please create an issue on our GitHub repository.
