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
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
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
Function to validate an emailvalidate(email: string): Promise<ValidationResponse>
Whether validation is in progress
result
ValidationResponse | null
Validation response (null if not yet validated). Contains success, error, statusCode, and data fields.
Error if validation failed
Reset state to initial values
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
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
}
}
};
Debounce Real-time Validation
Avoid validating on every keystroke:useEffect(() => {
const timeout = setTimeout(() => {
if (email) validate(email);
}, 500);
return () => clearTimeout(timeout);
}, [email]);
Next Steps