Skip to main content

Overview

The Vouch React SDK provides hooks and components for seamless email validation in React applications with automatic device fingerprinting.
Package: @vouch/react React Version: 16.8+ (hooks required) TypeScript: Full type definitions included

Installation

npm install @vouch/react

Quick Start

import { VouchProvider, useValidateEmail } from '@vouch/react';

function App() {
  return (
    <VouchProvider
      projectId="your-project-id"
      apiKey="your-client-api-key"
    >
      <SignupForm />
    </VouchProvider>
  );
}

function SignupForm() {
  const { validate, loading, result, error } = useValidateEmail();
  const [email, setEmail] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    await validate(email);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        required
      />
      <button disabled={loading}>
        {loading ? 'Validating...' : 'Sign Up'}
      </button>

      {result && !result.success && (
        <p className="error">{result.error || 'This email cannot be used'}</p>
      )}
      {result?.data && !result.data.checks.disposable?.pass && (
        <p className="error">Disposable emails are not allowed</p>
      )}
    </form>
  );
}

VouchProvider

Wrap your app with VouchProvider to enable Vouch throughout your component tree:
import { VouchProvider } from '@vouch/react';

function App() {
  return (
    <VouchProvider
      projectId={process.env.REACT_APP_VOUCH_PROJECT_ID}
      apiKey={process.env.REACT_APP_VOUCH_API_KEY}
      options={{
        endpoint: 'https://api.vouch.expert',
        version: 'latest',
        fingerprintOptions: {
          enableCanvas: true,
          enableWebGL: true,
          enableAudio: true,
          enableFonts: true
        }
      }}
    >
      <YourApp />
    </VouchProvider>
  );
}

Props

projectId
string
required
Your Vouch project ID
apiKey
string
required
Your client API key
options
object
Optional configuration:
  • endpoint - API endpoint URL (default: “https://api.vouch.expert”)
  • version - API version (default: “latest”)
  • fingerprintOptions - Fingerprint generation options
    • enableCanvas - Enable canvas fingerprinting
    • enableWebGL - Enable WebGL fingerprinting
    • enableAudio - Enable audio fingerprinting
    • enableFonts - Enable font fingerprinting

useValidateEmail()

Primary hook for email validation:
import { useValidateEmail } from '@vouch/react';

function SignupForm() {
  const {
    validate,
    loading,
    result,
    error,
    reset
  } = useValidateEmail();

  const handleValidate = async () => {
    const result = await validate('[email protected]');

    if (result.success && result.data) {
      // Check validation results
      const allChecksPassed = Object.values(result.data.checks).every(
        check => check.pass
      );

      if (allChecksPassed) {
        // Proceed with signup
      }
    }
  };

  return (
    <div>
      <button onClick={handleValidate} disabled={loading}>
        Validate
      </button>

      {loading && <Spinner />}
      {error && <Error message={error.message} />}
      {result && <Result data={result} />}
    </div>
  );
}

Return Values

validate
function
Function to validate an email
validate(email: string): Promise<ValidationResponse>
loading
boolean
Whether validation is in progress
result
ValidationResponse | null
Validation response (null if not yet validated). Contains success, error, statusCode, and data fields.
error
Error | null
Error if validation failed
reset
function
Reset state to initial values
reset(): void

useVouch()

Access the Vouch instance directly:
import { useVouch } from '@vouch/react';

function Component() {
  const vouch = useVouch();

  useEffect(() => {
    async function getFingerprint() {
      const fp = await vouch.getFingerprint();
      console.log('Device ID:', fp.visitorId);
    }

    getFingerprint();
  }, [vouch]);
}

Complete Examples

Full Signup Form

import { useState } from 'react';
import { VouchProvider, useValidateEmail } from '@vouch/react';

function App() {
  return (
    <VouchProvider projectId={PROJECT_ID} apiKey={API_KEY}>
      <SignupForm />
    </VouchProvider>
  );
}

function SignupForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { validate, loading, result } = useValidateEmail();

  const handleSubmit = async (e) => {
    e.preventDefault();

    // Validate email first
    const validation = await validate(email);

    if (!validation.success) {
      alert(validation.error || 'Validation failed');
      return;
    }

    if (validation.data) {
      const { checks, deviceData } = validation.data;

      // Check critical validations
      if (!checks.syntax?.pass || !checks.disposable?.pass) {
        alert('This email cannot be used');
        return;
      }

      // Check suspicious patterns
      if (deviceData && deviceData.emailsUsed > 5) {
        const proceed = confirm(
          'This device has been used with multiple emails. Continue anyway?'
        );
        if (!proceed) return;
      }
    }

    // Proceed with signup
    await createAccount({ email, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Email</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
        />
        {result?.data && !result.data.checks.disposable?.pass && (
          <p className="warning">Disposable email detected</p>
        )}
      </div>

      <div>
        <label>Password</label>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          required
        />
      </div>

      <button type="submit" disabled={loading}>
        {loading ? 'Validating...' : 'Sign Up'}
      </button>
    </form>
  );
}

Real-time Validation

import { useState, useEffect } from 'react';
import { useValidateEmail } from '@vouch/react';

function EmailInput() {
  const [email, setEmail] = useState('');
  const { validate, loading, result } = useValidateEmail();

  // Debounced validation
  useEffect(() => {
    if (!email.includes('@')) return;

    const timeout = setTimeout(() => {
      validate(email);
    }, 500);

    return () => clearTimeout(timeout);
  }, [email, validate]);

  return (
    <div className="email-input">
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter your email"
      />

      {loading && <Spinner />}

      {result && (
        <ValidationIndicator result={result} />
      )}
    </div>
  );
}

function ValidationIndicator({ result }) {
  if (!result) return null;

  if (!result.success) {
    return <span className="error">✗ Invalid email</span>;
  }

  if (result.data) {
    const { checks } = result.data;
    const allPassed = Object.values(checks).every(check => check.pass);

    if (allPassed) {
      return <span className="valid">✓ Valid email</span>;
    }

    if (!checks.syntax?.pass || !checks.disposable?.pass) {
      return <span className="error">✗ Invalid email</span>;
    }

    return <span className="warning">⚠ Suspicious email</span>;
  }

  return null;
}

TypeScript

import { useState } from 'react';
import { useValidateEmail, ValidationResponse } from '@vouch/react';

function SignupForm() {
  const [email, setEmail] = useState<string>('');
  const {
    validate,
    loading,
    result,
    error
  } = useValidateEmail();

  const handleValidate = async (): Promise<void> => {
    const validation: ValidationResponse = await validate(email);

    if (!validation.success) {
      console.log('Validation failed:', validation.error);
      return;
    }

    if (validation.data && !validation.data.checks.disposable?.pass) {
      console.log('Disposable email detected');
    }
  };

  return (
    <form onSubmit={handleValidate}>
      <input
        type="email"
        value={email}
        onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
          setEmail(e.target.value)
        }
      />
      <button disabled={loading}>Validate</button>
    </form>
  );
}

Next.js Integration

// app/layout.tsx (App Router)
import { VouchProvider } from '@vouch/react';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <VouchProvider
          projectId={process.env.NEXT_PUBLIC_VOUCH_PROJECT_ID!}
          apiKey={process.env.NEXT_PUBLIC_VOUCH_CLIENT_KEY!}
        >
          {children}
        </VouchProvider>
      </body>
    </html>
  );
}

// app/signup/page.tsx
'use client';

import { useValidateEmail } from '@vouch/react';

export default function SignupPage() {
  const { validate, loading, result } = useValidateEmail();

  // ... rest of component
}

Best Practices

Place VouchProvider at your app’s root for consistent access:
// ✓ Good
<VouchProvider>
  <App />
</VouchProvider>

// ✗ Bad - multiple providers
<Component>
  <VouchProvider>...</VouchProvider>
</Component>
Always show loading feedback during validation:
<button disabled={loading}>
  {loading ? 'Validating...' : 'Submit'}
</button>
Clear validation state after successful submission:
const { validate, reset } = useValidateEmail();

const handleSubmit = async (email) => {
  const result = await validate(email);

  if (result.success && result.data) {
    const allChecksPassed = Object.values(result.data.checks).every(
      check => check.pass
    );

    if (allChecksPassed) {
      await createAccount(email);
      reset(); // Clear state
    }
  }
};
Avoid validating on every keystroke:
useEffect(() => {
  const timeout = setTimeout(() => {
    if (email) validate(email);
  }, 500);

  return () => clearTimeout(timeout);
}, [email]);

Next Steps