Skip to main content

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:
cd ios && pod install

Android Setup

No additional setup required. The SDK includes all necessary native modules.

Permissions

Add internet permission to your AndroidManifest.xml (Android only):
AndroidManifest.xml
<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:
App.tsx
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.
loading
boolean
Whether a validation request is in progress
error
Error | null
Error that occurred during validation
validate
(email: string) => Promise<ValidationResponse>
Function to validate an email address
reset
() => void
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:
fingerprint
Fingerprint | null
Generated device fingerprint (null if not generated yet)
loading
boolean
Whether fingerprint generation is in progress
error
Error | null
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:
App.tsx
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>
  );
}

Form Integration

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:
projectId
string
required
Your Vouch project ID
apiKey
string
required
Your Vouch client API key
options
VouchOptions
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
}

Platform-Specific Considerations

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:
  1. Your app’s privacy policy
  2. App Store privacy nutrition label (iOS)
  3. Google Play Data Safety form (Android)
  4. 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: