> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vouch.expert/llms.txt
> Use this file to discover all available pages before exploring further.

# React SDK

> React hooks and components for email validation

## Overview

The Vouch React SDK provides hooks and components for seamless email validation in React applications with automatic device fingerprinting.

<Note>
  **Package:** `@vouch-in/react`
  **React Version:** 16.8+ (hooks required)
  **TypeScript:** Full type definitions included
</Note>

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @vouch-in/react
  ```

  ```bash yarn theme={null}
  yarn add @vouch-in/react
  ```

  ```bash pnpm theme={null}
  pnpm add @vouch-in/react
  ```
</CodeGroup>

## Quick Start

```jsx theme={null}
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:

```jsx theme={null}
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

<ParamField path="projectId" type="string" required>
  Your Vouch project ID
</ParamField>

<ParamField path="apiKey" type="string" required>
  Your client API key
</ParamField>

<ParamField path="options" type="object">
  Optional configuration:

  * `endpoint` - API endpoint URL (default: "[https://api.vouch.expert](https://api.vouch.expert)")
  * `apiVersion` - API version number or "latest" (default: "latest")
</ParamField>

## useValidateEmail()

Primary hook for email validation:

```jsx theme={null}
import { useValidateEmail } from '@vouch-in/react';

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

  const handleValidate = async () => {
    const result = await validate('user@example.com');

    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

<ResponseField name="validate" type="function">
  Function to validate an email. Returns the validation response.

  ```typescript theme={null}
  validate(email: string, options?: ValidationRequestOptions): Promise<ValidationResponse>
  ```
</ResponseField>

<ResponseField name="loading" type="boolean">
  Whether validation is in progress
</ResponseField>

<ResponseField name="data" type="ValidationResponse | null">
  Validation response (null if not yet validated). Contains `checks`, `metadata`, `recommendation`, and `signals`.
</ResponseField>

<ResponseField name="error" type="Error | null">
  Error if validation failed
</ResponseField>

<ResponseField name="reset" type="function">
  Reset state to initial values

  ```typescript theme={null}
  reset(): void
  ```
</ResponseField>

### ValidationResponse Type

```typescript theme={null}
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>;
}
```

<Note>
  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`.
</Note>

## useVouch()

Access the Vouch instance directly:

```jsx theme={null}
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:

```jsx theme={null}
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

### Full Signup Form

```jsx theme={null}
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

```jsx theme={null}
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

```tsx theme={null}
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

```jsx theme={null}
// 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

<AccordionGroup>
  <Accordion title="Wrap at Root Level" icon="sitemap">
    Place `VouchProvider` at your app's root for consistent access:

    ```jsx theme={null}
    // Good
    <VouchProvider>
      <App />
    </VouchProvider>

    // Bad - multiple providers
    <Component>
      <VouchProvider>...</VouchProvider>
    </Component>
    ```
  </Accordion>

  <Accordion title="Handle Loading States" icon="spinner">
    Always show loading feedback during validation:

    ```jsx theme={null}
    <button disabled={loading}>
      {loading ? 'Validating...' : 'Submit'}
    </button>
    ```
  </Accordion>

  <Accordion title="Reset After Submit" icon="rotate">
    Clear validation state after successful submission:

    ```jsx theme={null}
    const { validate, reset } = useValidateEmail();

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

      if (result.recommendation === 'allow') {
        await createAccount(email);
        reset(); // Clear state
      }
    };
    ```
  </Accordion>

  <Accordion title="Debounce Real-time Validation" icon="clock">
    Avoid validating on every keystroke:

    ```jsx theme={null}
    useEffect(() => {
      const timeout = setTimeout(() => {
        if (email) validate(email);
      }, 500);

      return () => clearTimeout(timeout);
    }, [email]);
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Next.js SDK" icon="N" href="/sdks/nextjs">
    Next.js-specific integration
  </Card>

  <Card title="JavaScript SDK" icon="js" href="/sdks/javascript">
    Vanilla JavaScript SDK
  </Card>

  <Card title="Best Practices" icon="star" href="/guides/best-practices">
    Production optimization
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/validate">
    Complete API docs
  </Card>
</CardGroup>
