Complete integration examples for React Native OpenVPN
Production-ready code samples and patterns for secure VPN integration
| Example | Description | Link |
|---|---|---|
| 🔌 Basic Connection | Simple connect/disconnect functionality | View → |
| ⚙️ Advanced Config | Enterprise-grade configuration options | View → |
| 📦 State Management | Minimal state management with Zustand | View → |
| 🚨 Error Handling | Comprehensive error management | View → |
Simple and reliable VPN connection patterns
import React, { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity, Alert, StyleSheet } from 'react-native';
import * as OpenVPN from 'react-native-openvpn';
const SimpleVPN = () => {
const [isConnected, setIsConnected] = useState(false);
const [isConnecting, setIsConnecting] = useState(false);
const [connectionState, setConnectionState] = useState(OpenVPN.ConnectionState.DISCONNECTED);
useEffect(() => {
// Set up state listener
const subscription = OpenVPN.addOpenVPNStateChangeListener((state) => {
setConnectionState(state.state);
setIsConnected(state.state === OpenVPN.ConnectionState.CONNECTED);
setIsConnecting(state.state === OpenVPN.ConnectionState.CONNECTING);
});
// Get initial state
OpenVPN.requestCurrentState();
return () => subscription.remove();
}, []);
const handleConnect = async () => {
try {
// Android permission check
if (Platform.OS === 'android') {
const isPrepared = await OpenVPN.isPrepared();
if (!isPrepared) {
const granted = await OpenVPN.prepare();
if (!granted) {
Alert.alert('Permission Required', 'VPN permission is required to continue');
return;
}
}
}
await OpenVPN.connect({
address: 'your-server.com',
username: 'your-username',
password: 'your-password',
openVPNConfig: yourConfigString,
iOSOptions: {
localizedDescription: 'My VPN',
networkExtensionBundleIdentifier: 'com.yourapp.vpn',
disconnectOnSleep: false,
onDemandEnabled: false,
},
androidOptions: {
Notification: {
openActivityPackageName: 'com.yourapp.MainActivity',
titleNotification: 'VPN Connected',
titleConnected: 'Secure connection active',
titleConnecting: 'Establishing secure connection...',
showDisconnectAction: true,
titleDisconnectButton: 'Disconnect',
},
},
});
} catch (error) {
Alert.alert('Connection Error', error.message);
}
};
const handleDisconnect = async () => {
try {
await OpenVPN.disconnect();
} catch (error) {
Alert.alert('Disconnect Error', error.message);
}
};
const getStatusColor = () => {
switch (connectionState) {
case OpenVPN.ConnectionState.CONNECTED: return '#4CAF50';
case OpenVPN.ConnectionState.CONNECTING: return '#FF9800';
case OpenVPN.ConnectionState.DISCONNECTING: return '#FF9800';
case OpenVPN.ConnectionState.ERROR: return '#F44336';
default: return '#9E9E9E';
}
};
const getStatusText = () => {
switch (connectionState) {
case OpenVPN.ConnectionState.CONNECTED: return '🟢 Connected';
case OpenVPN.ConnectionState.CONNECTING: return '🟡 Connecting...';
case OpenVPN.ConnectionState.DISCONNECTING: return '🟡 Disconnecting...';
case OpenVPN.ConnectionState.ERROR: return '🔴 Connection Error';
default: return '⚫ Disconnected';
}
};
return (
<View style={styles.container}>
<View style={styles.statusContainer}>
<Text style={[styles.statusText, { color: getStatusColor() }]}>
{getStatusText()}
</Text>
</View>
<View style={styles.buttonContainer}>
{!isConnected && !isConnecting && (
<TouchableOpacity style={styles.connectButton} onPress={handleConnect}>
<Text style={styles.buttonText}>Connect to VPN</Text>
</TouchableOpacity>
)}
{isConnecting && (
<TouchableOpacity style={styles.disabledButton} disabled>
<Text style={styles.buttonText}>Connecting...</Text>
</TouchableOpacity>
)}
{isConnected && (
<TouchableOpacity style={styles.disconnectButton} onPress={handleDisconnect}>
<Text style={styles.buttonText}>Disconnect VPN</Text>
</TouchableOpacity>
)}
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
statusContainer: {
marginBottom: 40,
padding: 20,
borderRadius: 10,
backgroundColor: '#f5f5f5',
},
statusText: {
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
buttonContainer: {
width: '100%',
},
connectButton: {
backgroundColor: '#4CAF50',
padding: 15,
borderRadius: 8,
alignItems: 'center',
},
disconnectButton: {
backgroundColor: '#F44336',
padding: 15,
borderRadius: 8,
alignItems: 'center',
},
disabledButton: {
backgroundColor: '#CCCCCC',
padding: 15,
borderRadius: 8,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
export default SimpleVPN;Enterprise-grade VPN configuration with advanced routing and security options
import React from 'react';
import * as OpenVPN from 'react-native-openvpn';
const EnterpriseVPN = () => {
const connectToEnterprise = async () => {
try {
await OpenVPN.connect({
address: 'enterprise-vpn.company.com',
username: 'employee@company.com',
password: 'secure-password',
openVPNConfig: enterpriseConfig,
iOSOptions: {
localizedDescription: 'Company VPN - Secure Access',
networkExtensionBundleIdentifier: 'com.company.app.vpn',
disconnectOnSleep: true,
onDemandEnabled: true,
includeAllNetworks: false,
excludeLocalNetworks: true,
excludeCellularServices: false,
persistTun: true,
connectionTimeout: 30,
googleDNSFallback: true,
autologinSessions: false,
retryOnAuthFailed: true,
},
androidOptions: {
// Use OpenVPN 2.x for better compatibility
useOpenVPN3: false,
compatibilityMode: OpenVPN.AndroidCompatibilityMode.OpenVPN_2_5_x,
// Network behavior
useSystemProxy: true,
useReconnectOnNetworkChange: true,
usePauseOnScreenOff: false,
useProfileEncryption: true,
// Custom DNS configuration
overrideDNS: true,
DNS1: '8.8.8.8',
DNS2: '1.1.1.1',
searchDomain: 'company.local',
// Advanced routing
useDefaultRoute: false,
customRoutes: '10.0.0.0/8 172.16.0.0/12 192.168.0.0/16',
excludedRoutes: '192.168.1.0/24 10.0.1.0/24',
allowLocalLAN: true,
blockUnusedAddressFamilies: true,
ignorePushedRoutes: false,
// IPv6 support
useDefaultRouteV6: false,
customRoutesV6: 'fd00::/8',
excludedRoutesV6: 'fe80::/10',
// App-specific routing
allowedVPNApps: [
'com.company.secure-app',
'com.company.internal-tools',
],
allowedVPNAppsAreDisallowed: false,
allowAppVpnBypass: false,
// Security settings
tlsProfileSecurity: OpenVPN.TLSSecurityProfile.PREFERRED,
expectServerTLSCert: true,
certificateHostnameCheck: true,
remoteCertificateSubject: 'CN=company-vpn-server',
useTLSAuth: true,
dataCiphers: 'AES-256-GCM:AES-128-GCM:AES-256-CBC',
packetDigests: 'SHA256:SHA1',
// Client behavior
persistTun: true,
pushPeerInfo: true,
useRandomHostname: false,
useFloat: false,
// Custom OpenVPN options
useCustomConfig: true,
customOptions: [
'verb 3',
'mute 20',
'keepalive 10 60',
'ping-timer-rem',
'persist-tun',
'persist-key',
].join('\n'),
// Reconnection strategy
connectRetryMax: '10',
connectRetryMaxTime: '300',
connectRetry: '5',
// Rich notification
Notification: {
openActivityPackageName: 'com.company.app.MainActivity',
titleNotification: 'Company VPN',
titleConnected: '🏢 Connected to Company Network',
titleConnecting: '🔄 Connecting to Corporate VPN...',
titleDisconnecting: '🔄 Disconnecting from VPN...',
titleDisconnected: '🔴 Disconnected from Company VPN',
titleError: '❌ VPN Connection Error',
showDisconnectAction: true,
titleDisconnectButton: 'Disconnect VPN',
showTimer: true,
},
},
});
console.log('Enterprise VPN connected successfully');
} catch (error) {
console.error('Enterprise VPN connection failed:', error);
throw error;
}
};
return null; // Your UI implementation
};Minimal and efficient VPN state management with Zustand
import { create } from 'zustand';
import * as OpenVPN from 'react-native-openvpn';
// 🏷️ Define VPN store state interface
interface VPNState {
// Connection state
connectionState: OpenVPN.ConnectionState;
isConnected: boolean;
isConnecting: boolean;
// Connection details
serverAddress: string | null;
lastError: string | null;
// Actions
setConnectionState: (state: OpenVPN.ConnectionState) => void;
setServerAddress: (address: string | null) => void;
setError: (error: string | null) => void;
clearError: () => void;
reset: () => void;
}
// 🎯 Create Zustand store
export const useVPNStore = create<VPNState>((set, get) => ({
// Initial state
connectionState: OpenVPN.ConnectionState.DISCONNECTED,
isConnected: false,
isConnecting: false,
serverAddress: null,
lastError: null,
// Actions
setConnectionState: (state: OpenVPN.ConnectionState) => set((prev) => {
const isConnected = state === OpenVPN.ConnectionState.CONNECTED;
const isConnecting = state === OpenVPN.ConnectionState.CONNECTING;
return {
connectionState: state,
isConnected,
isConnecting,
lastError: null, // Clear error on state change
};
}),
setServerAddress: (address: string | null) => set({ serverAddress: address }),
setError: (error: string | null) => set({
lastError: error,
connectionState: error ? OpenVPN.ConnectionState.ERROR : get().connectionState
}),
clearError: () => set({ lastError: null }),
reset: () => set({
connectionState: OpenVPN.ConnectionState.DISCONNECTED,
isConnected: false,
isConnecting: false,
serverAddress: null,
lastError: null,
}),
}));import React, { useEffect } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Platform } from 'react-native';
import * as OpenVPN from 'react-native-openvpn';
import { useVPNStore } from './vpn-store';
const VPNConnectionPage: React.FC = () => {
const {
connectionState,
isConnected,
isConnecting,
serverAddress,
lastError,
setConnectionState,
setServerAddress,
setError,
clearError,
} = useVPNStore();
// 🔗 Set up OpenVPN event listeners
useEffect(() => {
const subscription = OpenVPN.addOpenVPNStateChangeListener((event) => {
setConnectionState(event.state);
});
// 🔄 Request initial state
OpenVPN.requestCurrentState();
return () => subscription.remove();
}, [setConnectionState]);
// 🔌 Connect function
const handleConnect = async () => {
try {
clearError();
setServerAddress('your-server.com');
// Android permission check
if (Platform.OS === 'android') {
const isPrepared = await OpenVPN.isPrepared();
if (!isPrepared) {
const granted = await OpenVPN.prepare();
if (!granted) {
throw new Error('VPN permission denied');
}
}
}
await OpenVPN.connect({
address: 'your-server.com',
username: 'your-username',
password: 'your-password',
openVPNConfig: 'your-config-string',
iOSOptions: {
localizedDescription: 'My VPN',
networkExtensionBundleIdentifier: 'com.yourapp.vpn',
},
androidOptions: {
Notification: {
titleNotification: 'VPN Connected',
titleConnected: 'Secure connection active',
titleConnecting: 'Connecting to VPN...',
showDisconnectAction: true,
},
},
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Connection failed';
setError(errorMessage);
}
};
// 🔌 Disconnect function
const handleDisconnect = async () => {
try {
clearError();
await OpenVPN.disconnect();
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Disconnect failed';
setError(errorMessage);
}
};
// 🎨 Get status info
const getStatusInfo = () => {
if (lastError) {
return { emoji: '🔴', text: 'Error', color: '#F44336' };
}
switch (connectionState) {
case OpenVPN.ConnectionState.CONNECTED:
return { emoji: '🟢', text: 'Connected', color: '#4CAF50' };
case OpenVPN.ConnectionState.CONNECTING:
return { emoji: '🟡', text: 'Connecting...', color: '#FF9800' };
case OpenVPN.ConnectionState.DISCONNECTING:
return { emoji: '🟡', text: 'Disconnecting...', color: '#FF9800' };
default:
return { emoji: '⚫', text: 'Disconnected', color: '#9E9E9E' };
}
};
const { emoji, text, color } = getStatusInfo();
return (
<View style={styles.container}>
{/* Status Display */}
<View style={[styles.statusCard, { borderColor: color }]}>
<Text style={styles.statusEmoji}>{emoji}</Text>
<Text style={[styles.statusText, { color }]}>{text}</Text>
{serverAddress && (
<Text style={styles.serverText}>Server: {serverAddress}</Text>
)}
</View>
{/* Error Display */}
{lastError && (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>❌ {lastError}</Text>
<TouchableOpacity onPress={clearError} style={styles.clearButton}>
<Text style={styles.clearButtonText}>Clear</Text>
</TouchableOpacity>
</View>
)}
{/* Action Buttons */}
<View style={styles.buttonContainer}>
{!isConnected && !isConnecting && (
<TouchableOpacity style={styles.connectButton} onPress={handleConnect}>
<Text style={styles.buttonText}>🔌 Connect VPN</Text>
</TouchableOpacity>
)}
{isConnecting && (
<TouchableOpacity style={styles.disabledButton} disabled>
<Text style={styles.buttonText}>🔄 Connecting...</Text>
</TouchableOpacity>
)}
{isConnected && (
<TouchableOpacity style={styles.disconnectButton} onPress={handleDisconnect}>
<Text style={styles.buttonText}>🔌 Disconnect VPN</Text>
</TouchableOpacity>
)}
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
statusCard: {
backgroundColor: 'white',
padding: 24,
borderRadius: 12,
marginBottom: 20,
borderWidth: 2,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
statusEmoji: {
fontSize: 36,
marginBottom: 8,
},
statusText: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 8,
},
serverText: {
fontSize: 14,
color: '#666',
},
errorContainer: {
backgroundColor: '#ffebee',
padding: 12,
borderRadius: 8,
marginBottom: 20,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
errorText: {
color: '#c62828',
flex: 1,
},
clearButton: {
paddingHorizontal: 12,
paddingVertical: 4,
},
clearButtonText: {
color: '#1976d2',
fontWeight: 'bold',
},
buttonContainer: {
gap: 12,
},
connectButton: {
backgroundColor: '#4CAF50',
padding: 16,
borderRadius: 8,
alignItems: 'center',
},
disconnectButton: {
backgroundColor: '#F44336',
padding: 16,
borderRadius: 8,
alignItems: 'center',
},
disabledButton: {
backgroundColor: '#CCCCCC',
padding: 16,
borderRadius: 8,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
export default VPNConnectionPage;Comprehensive error management and user-friendly error recovery patterns
import React, { useState } from 'react';
import { Alert } from 'react-native';
import * as OpenVPN from 'react-native-openvpn';
const VPNWithErrorHandling = () => {
const [errors, setErrors] = useState<string[]>([]);
const handleVPNError = (error: any, context: string) => {
let errorMessage = 'Unknown error occurred';
if (error instanceof Error) {
errorMessage = error.message;
} else if (typeof error === 'string') {
errorMessage = error;
}
const fullError = `${context}: ${errorMessage}`;
setErrors(prev => [...prev, fullError]);
console.error(fullError, error);
// Show user-friendly error messages
switch (context) {
case 'CONNECTION':
Alert.alert(
'Connection Failed',
'Unable to connect to VPN server. Please check your credentials and try again.',
[{ text: 'OK' }]
);
break;
case 'PERMISSION':
Alert.alert(
'Permission Required',
'VPN permission is required to establish connection.',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Grant Permission', onPress: () => OpenVPN.prepare() },
]
);
break;
case 'NETWORK':
Alert.alert(
'Network Error',
'Please check your internet connection and try again.',
[{ text: 'OK' }]
);
break;
default:
Alert.alert('Error', errorMessage, [{ text: 'OK' }]);
}
};
const safeConnect = async (config: OpenVPN.ConnectionParams) => {
try {
// Android permission check with error handling
if (Platform.OS === 'android') {
try {
const isPrepared = await OpenVPN.isPrepared();
if (!isPrepared) {
const granted = await OpenVPN.prepare();
if (!granted) {
throw new Error('User denied VPN permission');
}
}
} catch (permError) {
handleVPNError(permError, 'PERMISSION');
return;
}
}
// Validate configuration
if (!config.address || !config.username || !config.password) {
throw new Error('Missing required connection parameters');
}
if (!config.openVPNConfig && !config.openVPNConfigLocalFile) {
throw new Error('OpenVPN configuration is required');
}
// Attempt connection with timeout
const connectionPromise = OpenVPN.connect(config);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Connection timeout')), 30000);
});
await Promise.race([connectionPromise, timeoutPromise]);
} catch (connectionError) {
// Categorize errors
const errorMessage = connectionError.message.toLowerCase();
if (errorMessage.includes('network') || errorMessage.includes('timeout')) {
handleVPNError(connectionError, 'NETWORK');
} else if (errorMessage.includes('auth') || errorMessage.includes('credential')) {
handleVPNError(connectionError, 'AUTHENTICATION');
} else if (errorMessage.includes('config') || errorMessage.includes('invalid')) {
handleVPNError(connectionError, 'CONFIGURATION');
} else {
handleVPNError(connectionError, 'CONNECTION');
}
}
};
const safeDisconnect = async () => {
try {
await OpenVPN.disconnect();
} catch (disconnectError) {
handleVPNError(disconnectError, 'DISCONNECTION');
}
};
// Clear errors
const clearErrors = () => {
setErrors([]);
};
return (
<View>
{/* Error display */}
{errors.length > 0 && (
<View style={styles.errorContainer}>
<Text style={styles.errorTitle}>Recent Errors:</Text>
{errors.slice(-3).map((error, index) => (
<Text key={index} style={styles.errorText}>
• {error}
</Text>
))}
<TouchableOpacity onPress={clearErrors}>
<Text style={styles.clearButton}>Clear Errors</Text>
</TouchableOpacity>
</View>
)}
{/* Your VPN UI */}
</View>
);
};
const styles = StyleSheet.create({
errorContainer: {
backgroundColor: '#ffebee',
padding: 10,
margin: 10,
borderRadius: 5,
borderLeftWidth: 4,
borderLeftColor: '#f44336',
},
errorTitle: {
fontWeight: 'bold',
color: '#c62828',
marginBottom: 5,
},
errorText: {
color: '#d32f2f',
fontSize: 12,
marginBottom: 2,
},
clearButton: {
color: '#1976d2',
textAlign: 'right',
marginTop: 5,
textDecorationLine: 'underline',
},
});Essential VPN integration patterns for React Native applications
This guide provides practical examples for integrating React Native OpenVPN into your applications. Each example includes:
| 🔒 Security | Best practices and secure configuration patterns |
| 🎯 State Management | Simple and efficient state management with Zustand |
| 🚨 Error Handling | Comprehensive error management with user-friendly recovery |
| 📱 Cross-Platform | iOS and Android specific configurations and optimizations |
| 🎨 UI/UX | Clean, responsive interfaces with real-time status indicators |
🚀 Next Steps:
- Explore the API Reference for detailed method documentation
- Check Installation Guide for platform-specific setup
- Review Troubleshooting Guide for common issues and solutions
Built with ❤️ for secure, reliable VPN integration in React Native applications