Overview
The Vouch React Native SDK provides email validation and device fingerprinting for React Native applications. It uses native modules for optimal signal collection and performance.
Package: @vouch-in/react-native
Platform: iOS 15.0+ & Android 8.0+ (API 26+)
React Native: 0.60.0+
Distribution: npm/yarn/pnpm
Installation
npm install @vouch-in/react-native
iOS Setup
Install iOS dependencies:
Android Setup
No additional setup required. The SDK includes all necessary native modules.
Permissions
Add internet permission to your AndroidManifest.xml (Android only):
<uses-permission android:name="android.permission.INTERNET" />
No permissions required on iOS. The SDK only needs internet access on Android.
Quick Start
Wrap Your App
Wrap your app with VouchProvider:
import { VouchProvider } from '@vouch-in/react-native';
export default function App() {
return (
<VouchProvider
projectId="your-project-id"
apiKey="your-client-api-key"
>
<YourApp />
</VouchProvider>
);
}
Validate Email
Use the useValidateEmail hook:
import { useValidateEmail } from '@vouch-in/react-native';
import { View, TextInput, Button, Text } from 'react-native';
import { useState } from 'react';
function EmailForm() {
const { validate, loading, data } = useValidateEmail();
const [email, setEmail] = useState('');
const handleSubmit = async () => {
const result = await validate(email);
if (result.recommendation === 'allow') {
console.log('Email is valid!');
} else {
// Check specific validations
if (!result.checks.syntax?.pass) {
console.log('Invalid email format');
} else if (!result.checks.disposable?.pass) {
console.log('Disposable emails not allowed');
}
}
};
return (
<View>
<TextInput
value={email}
onChangeText={setEmail}
placeholder="Email address"
keyboardType="email-address"
autoCapitalize="none"
editable={!loading}
/>
<Button
title={loading ? 'Validating...' : 'Validate'}
onPress={handleSubmit}
disabled={loading}
/>
{data && (
<Text>
{data.recommendation === 'allow' ? 'Valid' : 'Invalid'}
</Text>
)}
</View>
);
}
Hooks
useValidateEmail
Hook for email validation with loading and error states:
import { useValidateEmail } from '@vouch-in/react-native';
import { View, TextInput, Button, Text, ActivityIndicator } from 'react-native';
import { useState } from 'react';
function SignUpForm() {
const { validate, loading, data, error, reset } = useValidateEmail();
const [email, setEmail] = useState('');
const handleValidate = async () => {
const result = await validate(email);
if (result.recommendation === 'allow') {
// Proceed with sign-up
console.log('Valid email, previous signups:', result.metadata.previousSignups);
}
};
return (
<View style={{ padding: 20 }}>
<TextInput
value={email}
onChangeText={(text) => {
setEmail(text);
reset(); // Clear previous results
}}
placeholder="Enter your email"
keyboardType="email-address"
autoCapitalize="none"
style={{
borderWidth: 1,
borderColor: '#ccc',
padding: 10,
borderRadius: 5,
}}
/>
<Button
title={loading ? 'Validating...' : 'Sign Up'}
onPress={handleValidate}
disabled={loading || !email}
/>
{loading && <ActivityIndicator style={{ marginTop: 10 }} />}
{data && !loading && (
<Text
style={{
marginTop: 10,
color: data.recommendation === 'allow' ? 'green' : 'red',
}}
>
{data.recommendation === 'allow'
? 'Email is valid'
: 'Email validation failed'}
</Text>
)}
</View>
);
}
Returns:
data
ValidationResponse | null
Validation response (null if not validated yet). Contains checks, metadata, recommendation, and signals.
Whether a validation request is in progress
Error that occurred during validation
validate
(email: string) => Promise<ValidationResponse>
Function to validate an email address
Reset the validation state
useFingerprint
Hook to generate device fingerprint:
import { useFingerprint } from '@vouch-in/react-native';
import { View, Button, Text, ScrollView } from 'react-native';
function DeviceInfo() {
const { fingerprint, loading, error, generate } = useFingerprint();
return (
<View style={{ padding: 20 }}>
<Button
title={loading ? 'Generating...' : 'Generate Fingerprint'}
onPress={generate}
disabled={loading}
/>
{error && <Text style={{ color: 'red' }}>Error: {error.message}</Text>}
{fingerprint && (
<ScrollView style={{ marginTop: 20 }}>
<Text>Device: {fingerprint.hardware.deviceModel}</Text>
<Text>Screen: {fingerprint.hardware.screenWidth}x{fingerprint.hardware.screenHeight}</Text>
<Text>OS: {fingerprint.system.osVersion}</Text>
<Text>Fonts: {fingerprint.fonts.fonts?.length || 0}</Text>
</ScrollView>
)}
</View>
);
}
Returns:
Generated device fingerprint (null if not generated yet)
Whether fingerprint generation is in progress
Error that occurred during generation
generate
() => Promise<Fingerprint>
Function to generate device fingerprint
useVouch
Hook to access the Vouch instance directly:
import { useVouch } from '@vouch-in/react-native';
function CustomComponent() {
const vouch = useVouch();
const handleCustomValidation = async () => {
const result = await vouch.validate('[email protected]');
console.log(result.recommendation);
};
return (
<Button title="Custom Validation" onPress={handleCustomValidation} />
);
}
Returns: Vouch instance
Advanced Usage
Real-Time Validation
Validate as the user types with debouncing:
import { useValidateEmail } from '@vouch-in/react-native';
import { View, TextInput, Text } from 'react-native';
import { useState, useEffect, useRef } from 'react';
function RealtimeEmailInput() {
const { validate, loading, data } = useValidateEmail();
const [email, setEmail] = useState('');
const timeoutRef = useRef<NodeJS.Timeout>();
useEffect(() => {
if (!email || !email.includes('@')) return;
// Clear previous timeout
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
// Debounce validation
timeoutRef.current = setTimeout(() => {
validate(email);
}, 500);
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, [email]);
const getStatusIndicator = () => {
if (loading) return '...';
if (!data) return '';
return data.recommendation === 'allow' ? '✓' : '✗';
};
return (
<View style={{ padding: 20 }}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TextInput
value={email}
onChangeText={setEmail}
placeholder="Email address"
keyboardType="email-address"
autoCapitalize="none"
style={{
flex: 1,
borderWidth: 1,
borderColor: data?.recommendation === 'allow' ? 'green' : '#ccc',
padding: 10,
borderRadius: 5,
}}
/>
<Text style={{ marginLeft: 10, fontSize: 18 }}>
{getStatusIndicator()}
</Text>
</View>
{data && data.recommendation !== 'allow' && !loading && (
<Text style={{ color: 'red', marginTop: 5 }}>
{!data.checks.syntax?.pass
? 'Invalid email format'
: 'Email validation failed'}
</Text>
)}
</View>
);
}
Custom Options
Configure the SDK with custom options:
import { VouchProvider } from '@vouch-in/react-native';
export default function App() {
return (
<VouchProvider
projectId="your-project-id"
apiKey="your-client-api-key"
options={{
endpoint: 'https://api.vouch.expert',
apiVersion: 1, // Use /v1/ endpoint
}}
>
<YourApp />
</VouchProvider>
);
}
Integrate with your signup form:
import { useValidateEmail } from '@vouch-in/react-native';
import { View, TextInput, Button, Text } from 'react-native';
import { useState } from 'react';
function RegistrationForm() {
const { validate, loading } = useValidateEmail();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [emailError, setEmailError] = useState<string | null>(null);
const [formSubmitting, setFormSubmitting] = useState(false);
const handleSubmit = async () => {
setFormSubmitting(true);
setEmailError(null);
// Validate email first
const result = await validate(email);
if (result.recommendation !== 'allow') {
if (!result.checks.syntax?.pass) {
setEmailError('Invalid email format');
} else if (!result.checks.disposable?.pass) {
setEmailError('Disposable emails not allowed');
} else {
setEmailError('Email validation failed');
}
setFormSubmitting(false);
return;
}
// Proceed with registration
try {
await registerUser(email, password);
console.log('Registration successful');
} catch (error) {
console.error('Registration failed:', error);
} finally {
setFormSubmitting(false);
}
};
return (
<View style={{ padding: 20 }}>
<TextInput
value={email}
onChangeText={setEmail}
placeholder="Email"
keyboardType="email-address"
autoCapitalize="none"
style={{
borderWidth: 1,
borderColor: emailError ? 'red' : '#ccc',
padding: 10,
borderRadius: 5,
marginBottom: 10,
}}
/>
{emailError && (
<Text style={{ color: 'red', marginBottom: 10 }}>{emailError}</Text>
)}
<TextInput
value={password}
onChangeText={setPassword}
placeholder="Password"
secureTextEntry
style={{
borderWidth: 1,
borderColor: '#ccc',
padding: 10,
borderRadius: 5,
marginBottom: 20,
}}
/>
<Button
title={loading || formSubmitting ? 'Submitting...' : 'Register'}
onPress={handleSubmit}
disabled={loading || formSubmitting || !email || !password}
/>
</View>
);
}
async function registerUser(email: string, password: string) {
// Your registration logic here
}
API Reference
VouchProvider
Provider component that initializes the Vouch SDK.
<VouchProvider
projectId="your-project-id"
apiKey="your-client-api-key"
options={{
endpoint: "https://api.vouch.expert",
apiVersion: 1,
}}
>
{children}
</VouchProvider>
Props:
Your Vouch client API key
Optional SDK configuration:
endpoint - API endpoint URL (default: “https://api.vouch.expert”)
apiVersion - API version number or “latest” (default: “latest”)
ValidationResponse
Email validation response:
interface ValidationResponse {
checks: Record<string, CheckResult>;
metadata: {
fingerprintHash: string | null;
previousSignups: number;
totalLatency: number;
};
recommendation: 'allow' | 'block' | 'flag';
signals: string[];
}
interface CheckResult {
pass: boolean;
error?: string;
latency: number;
metadata?: Record<string, unknown>;
}
Fingerprint
Device fingerprint data:
interface Fingerprint {
hardware: HardwareSignals; // Device hardware info
fonts: FontSignals; // System fonts
system: SystemSignals; // OS info
storage: StorageSignals; // Storage availability
}
iOS
- No permissions required
- Uses native Swift SDK for fingerprinting
- Supports iOS 15.0+
Android
- Requires INTERNET permission
- Uses native Kotlin SDK for fingerprinting
- Supports Android 8.0+ (API 26+)
- ProGuard/R8 compatible (rules included automatically)
Error Handling
Always handle validation errors gracefully:
const { validate, loading, data, error } = useValidateEmail();
const handleValidate = async () => {
try {
const result = await validate(email);
if (result.recommendation !== 'allow') {
// Email validation failed
showError(!result.checks.syntax?.pass
? 'Invalid email format'
: 'Please use a different email');
return;
}
// Email is valid
proceedWithSignup();
} catch (err) {
// Network or SDK error
console.error('Validation error:', err);
showToast('Validation unavailable, please try again');
}
};
The SDK generates fingerprint on-demand during validation to optimize mobile performance and battery usage.
Privacy & Permissions
Data Collection
The SDK collects device fingerprint data for fraud prevention. You must disclose this in:
- Your app’s privacy policy
- App Store privacy nutrition label (iOS)
- Google Play Data Safety form (Android)
- GDPR/CCPA notices (if applicable)
What data is collected:
- Hardware signals: Screen dimensions, CPU cores, memory, device model
- Font signals: System fonts and SHA-256 hash
- System signals: OS version, language, locale, timezone
- Storage signals: Storage availability checks
No personally identifiable information (PII) is collected. All data is technical device and system information.
Best Practices
1. Initialize Early
Place VouchProvider at the top level of your app:
// Good - Top level
<VouchProvider projectId="..." apiKey="...">
<NavigationContainer>
<App />
</NavigationContainer>
</VouchProvider>
// Bad - Too deep
<NavigationContainer>
<VouchProvider projectId="..." apiKey="...">
<App />
</VouchProvider>
</NavigationContainer>
2. Handle Errors Gracefully
Don’t block users due to validation failures:
const result = await validate(email);
if (result.recommendation !== 'allow') {
// Show error but allow retry
showError('Please check your email');
} else {
// Proceed
submitForm();
}
3. Use Debouncing for Real-Time Validation
Avoid excessive API calls:
useEffect(() => {
const timeout = setTimeout(() => {
if (email) validate(email);
}, 500); // 500ms debounce
return () => clearTimeout(timeout);
}, [email]);
Support
For issues and questions: