> ## 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.

# Next.js SDK

> Full-stack email validation for Next.js applications

## Overview

The Vouch Next.js SDK provides seamless integration for both client and server components with automatic fingerprinting and optimized for Next.js App Router and Pages Router.

<Note>
  **Package:** `@vouch-in/next`
  **Next.js Version:** 13+ (App Router) or 12+ (Pages Router)
  **TypeScript:** Full type definitions included
</Note>

## Installation

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

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

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

## App Router (Next.js 13+)

### Server Component

```typescript theme={null}
// app/api/validate/route.ts
import { Vouch } from '@vouch-in/next';

const vouch = new Vouch(
  process.env.VOUCH_PROJECT_ID!,
  process.env.VOUCH_SERVER_KEY!
);

export async function POST(request: Request) {
  const { email, fingerprintHash } = await request.json();

  const result = await vouch.validate(email, {
    ip: request.headers.get('x-forwarded-for') || undefined,
    userAgent: request.headers.get('user-agent') || undefined,
    fingerprintHash, // Optional: from client SDK
  });

  // Check the recommendation
  if (result.recommendation !== 'allow') {
    return Response.json({ error: result.message }, { status: 400 });
  }

  return Response.json({ success: true, recommendation: result.recommendation });
}
```

### Client Component

```typescript theme={null}
// app/signup/page.tsx
'use client';

import { useState } from 'react';
import { VouchProvider, useValidateEmail } from '@vouch-in/next/client';

export default function SignupPage() {
  return (
    <VouchProvider
      projectId={process.env.NEXT_PUBLIC_VOUCH_PROJECT_ID!}
      apiKey={process.env.NEXT_PUBLIC_VOUCH_CLIENT_KEY!}
    >
      <SignupForm />
    </VouchProvider>
  );
}

function SignupForm() {
  const { validate, loading, data } = 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)}
      />
      <button disabled={loading}>
        {loading ? 'Validating...' : 'Sign Up'}
      </button>
      {data && data.recommendation !== 'allow' && (
        <p className="error">Email validation failed</p>
      )}
    </form>
  );
}
```

## Pages Router (Next.js 12)

### API Route

```typescript theme={null}
// pages/api/validate.ts
import { Vouch } from '@vouch-in/next';
import type { NextApiRequest, NextApiResponse } from 'next';

const vouch = new Vouch(
  process.env.VOUCH_PROJECT_ID!,
  process.env.VOUCH_SERVER_KEY!
);

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { email, fingerprintHash } = req.body;

  const result = await vouch.validate(email, {
    ip: req.socket.remoteAddress,
    userAgent: req.headers['user-agent'],
    fingerprintHash,
  });

  if (result.recommendation !== 'allow') {
    return res.status(400).json({
      error: 'Email validation failed',
      recommendation: result.recommendation
    });
  }

  res.status(200).json({ success: true, recommendation: result.recommendation });
}
```

### Client Page

```tsx theme={null}
// pages/signup.tsx
import { useState } from 'react';
import { VouchProvider, useValidateEmail } from '@vouch-in/next/client';

export default function SignupPage() {
  return (
    <VouchProvider
      projectId={process.env.NEXT_PUBLIC_VOUCH_PROJECT_ID!}
      apiKey={process.env.NEXT_PUBLIC_VOUCH_CLIENT_KEY!}
    >
      <SignupForm />
    </VouchProvider>
  );
}

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

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

    if (result.recommendation === 'allow') {
      // All checks passed
      await submitSignup(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>
    </form>
  );
}
```

## Environment Variables

```bash theme={null}
# .env.local
VOUCH_PROJECT_ID=your_project_id
VOUCH_SERVER_KEY=your_server_key

NEXT_PUBLIC_VOUCH_PROJECT_ID=your_project_id
NEXT_PUBLIC_VOUCH_CLIENT_KEY=your_client_key
```

<Warning>
  Use `NEXT_PUBLIC_` prefix only for client keys. Never expose server keys!
</Warning>

## Middleware

Validate emails in Next.js middleware:

```typescript theme={null}
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { Vouch } from '@vouch-in/next';

const vouch = new Vouch(
  process.env.VOUCH_PROJECT_ID!,
  process.env.VOUCH_SERVER_KEY!
);

export async function middleware(request: NextRequest) {
  if (request.nextUrl.pathname === '/api/signup') {
    const body = await request.json();
    const { email } = body;

    const result = await vouch.validate(email, {
      ip: request.ip,
      userAgent: request.headers.get('user-agent') || undefined,
    });

    if (result.recommendation === 'block') {
      return NextResponse.json(
        { error: 'Email blocked' },
        { status: 400 }
      );
    }

    if (!result.checks.syntax?.pass) {
      return NextResponse.json(
        { error: 'Invalid email format' },
        { status: 400 }
      );
    }
  }

  return NextResponse.next();
}
```

## Server Actions (Next.js 14+)

```typescript theme={null}
// app/actions.ts
'use server';

import { Vouch } from '@vouch-in/next';

const vouch = new Vouch(
  process.env.VOUCH_PROJECT_ID!,
  process.env.VOUCH_SERVER_KEY!
);

export async function validateEmail(email: string) {
  const result = await vouch.validate(email);

  if (result.recommendation !== 'allow') {
    return { success: false, error: 'Email validation failed' };
  }

  if (!result.checks.disposable?.pass) {
    return { success: false, error: 'Disposable emails not allowed' };
  }

  return { success: true, metadata: result.metadata };
}
```

```tsx theme={null}
// app/signup/page.tsx
'use client';

import { validateEmail } from './actions';

export default function SignupForm() {
  async function handleSubmit(formData: FormData) {
    const email = formData.get('email') as string;
    const result = await validateEmail(email);

    if (!result.success) {
      alert(result.error);
      return;
    }

    // Proceed with signup
  }

  return (
    <form action={handleSubmit}>
      <input type="email" name="email" required />
      <button type="submit">Sign Up</button>
    </form>
  );
}
```

## API Types

```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>

## Next Steps

<CardGroup cols={2}>
  <Card title="React SDK" icon="react" href="/sdks/react">
    React hooks details
  </Card>

  <Card title="Node.js SDK" icon="node" href="/sdks/node">
    Server-side SDK
  </Card>
</CardGroup>
