Overview
The Vouch React SDK provides hooks and components for seamless email validation in React applications with automatic device fingerprinting.
Package: @vouch-in/react
React Version: 16.8+ (hooks required)
TypeScript: Full type definitions included
Installation
npm install @vouch-in/react
Quick Start
import { VouchProvider , useValidateEmail } from '@vouch-in/react' ;
function App () {
return (
< VouchProvider
projectId = "your-project-id"
apiKey = "your-client-api-key"
>
< SignupForm />
</ VouchProvider >
);
}
function SignupForm () {
const { validate , loading , data , error } = useValidateEmail ();
const [ email , setEmail ] = useState ( '' );
const handleSubmit = async ( e ) => {
e . preventDefault ();
const result = await validate ( email );
if ( result . recommendation === 'allow' ) {
// Proceed with signup
console . log ( 'Email is valid!' );
}
};
return (
< form onSubmit = { handleSubmit } >
< input
type = "email"
value = { email }
onChange = { ( e ) => setEmail ( e . target . value ) }
required
/>
< button disabled = { loading } >
{ loading ? 'Validating...' : 'Sign Up' }
</ button >
{ data && data . recommendation !== 'allow' && (
< p className = "error" > { data . message } </ p >
) }
{ error && (
< p className = "error" > { error . message } </ p >
) }
</ form >
);
}
VouchProvider
Wrap your app with VouchProvider to enable Vouch throughout your component tree:
import { VouchProvider } from '@vouch-in/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' ,
apiVersion: 'latest' , // or 1, 2, etc.
} }
>
< YourApp />
</ VouchProvider >
);
}
Props
Optional configuration:
endpoint - API endpoint URL (default: “https://api.vouch.expert ”)
apiVersion - API version number or “latest” (default: “latest”)
useValidateEmail()
Primary hook for email validation:
import { useValidateEmail } from '@vouch-in/react' ;
function SignupForm () {
const {
validate ,
loading ,
data ,
error ,
reset
} = useValidateEmail ();
const handleValidate = async () => {
const result = await validate ( '[email protected] ' );
if ( result . recommendation === 'allow' ) {
// All checks passed, proceed with signup
proceedWithSignup ();
} else {
// Check individual validation results
if ( ! result . checks . disposable ?. pass ) {
showError ( 'Disposable emails are not allowed' );
}
}
};
return (
< div >
< button onClick = { handleValidate } disabled = { loading } >
Validate
</ button >
{ loading && < Spinner /> }
{ error && < Error message = { error . message } /> }
{ data && < ValidationResult data = { data } /> }
</ div >
);
}
Return Values
Function to validate an email. Returns the validation response. validate ( email : string , options ?: ValidationRequestOptions ): Promise < ValidationResponse >
Whether validation is in progress
data
ValidationResponse | null
Validation response (null if not yet validated). Contains checks, metadata, recommendation, and signals.
Error if validation failed
Reset state to initial values
ValidationResponse Type
interface ValidationResponse {
checks : Record < string , CheckResult >;
message : string | null ;
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 >;
}
The message field contains a human-friendly error message when the recommendation is flag or block (e.g. “Please use a permanent email address, not a temporary one.”). It is null when the recommendation is allow.
useVouch()
Access the Vouch instance directly:
import { useVouch } from '@vouch-in/react' ;
function Component () {
const vouch = useVouch ();
useEffect (() => {
async function getFingerprint () {
const fp = await vouch . generateFingerprint ();
console . log ( 'Canvas hash:' , fp . canvas . hash );
}
getFingerprint ();
}, [ vouch ]);
}
useFingerprint()
Hook to generate and access device fingerprint:
import { useFingerprint } from '@vouch-in/react' ;
function DeviceInfo () {
const { fingerprint , loading , error , generate } = useFingerprint ();
return (
< div >
< button onClick = { generate } disabled = { loading } >
Generate Fingerprint
</ button >
{ fingerprint && (
< div >
< p > Canvas: { fingerprint . canvas . hash } </ p >
< p > WebGL: { fingerprint . webgl . hash } </ p >
< p > Screen: { fingerprint . hardware . screen . width } x { fingerprint . hardware . screen . height } </ p >
</ div >
) }
</ div >
);
}
Complete Examples
import { useState } from 'react' ;
import { VouchProvider , useValidateEmail } from '@vouch-in/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 , data } = useValidateEmail ();
const handleSubmit = async ( e ) => {
e . preventDefault ();
// Validate email first
const result = await validate ( email );
const { checks , metadata , recommendation } = result ;
// Check critical validations
if ( ! checks . syntax ?. pass ) {
alert ( 'Invalid email format' );
return ;
}
if ( ! checks . disposable ?. pass ) {
alert ( 'Disposable emails are not allowed' );
return ;
}
// Check suspicious patterns
if ( metadata . previousSignups > 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
/>
{ data && ! 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-in/react' ;
function EmailInput () {
const [ email , setEmail ] = useState ( '' );
const { validate , loading , data } = 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 /> }
{ data && (
< ValidationIndicator data = { data } />
) }
</ div >
);
}
function ValidationIndicator ({ data }) {
if ( ! data ) return null ;
const { checks , recommendation } = data ;
if ( recommendation === 'allow' ) {
return < span className = "valid" > Valid email </ span > ;
}
if ( ! checks . syntax ?. pass ) {
return < span className = "error" > Invalid email format </ span > ;
}
if ( ! checks . disposable ?. pass ) {
return < span className = "error" > Disposable emails not allowed </ span > ;
}
return < span className = "warning" > Email flagged for review </ span > ;
}
TypeScript
import { useState } from 'react' ;
import { useValidateEmail , ValidationResponse } from '@vouch-in/react' ;
function SignupForm () {
const [ email , setEmail ] = useState < string >( '' );
const {
validate ,
loading ,
data ,
error
} = useValidateEmail ();
const handleValidate = async () : Promise < void > => {
const result : ValidationResponse = await validate ( email );
if ( result . recommendation !== 'allow' ) {
console . log ( 'Email flagged:' , result . recommendation );
return ;
}
if ( ! result . checks . disposable ?. pass ) {
console . log ( 'Disposable email detected' );
}
// Proceed with valid email
await submitForm ( email );
};
return (
< form onSubmit = { ( e ) => { e . preventDefault (); 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-in/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-in/react' ;
export default function SignupPage () {
const { validate , loading , data } = 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 . recommendation === 'allow' ) {
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