Skip to main content

Overview

The Vouch Remix SDK provides seamless integration with Remix’s action/loader pattern for both client and server-side email validation.
Package: @vouch/remix Remix Version: 1.0+ TypeScript: Full type definitions included

Installation

npm install @vouch/remix

Server-Side Validation

Action Handler

// app/routes/signup.tsx
import { json, type ActionArgs } from '@remix-run/node';
import { Vouch } from '@vouch/remix';

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

export async function action({ request }: ActionArgs) {
  const formData = await request.formData();
  const email = formData.get('email') as string;

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

  if (!result.success) {
    return json({
      error: result.error || 'Validation failed'
    }, { status: result.statusCode || 400 });
  }

  if (result.data && (!result.data.checks.syntax?.pass || !result.data.checks.disposable?.pass)) {
    return json({
      error: 'This email address cannot be used'
    }, { status: 400 });
  }

  // Create user
  const user = await createUser(email);

  return json({ user });
}

Client Component

import { Form, useActionData, useNavigation } from '@remix-run/react';

export default function SignupRoute() {
  const actionData = useActionData<typeof action>();
  const navigation = useNavigation();
  const isSubmitting = navigation.state === 'submitting';

  return (
    <Form method="post">
      <input type="email" name="email" required />
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Validating...' : 'Sign Up'}
      </button>

      {actionData?.error && (
        <p className="error">{actionData.error}</p>
      )}
    </Form>
  );
}

Client-Side Validation

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

export default function SignupPage() {
  return (
    <VouchProvider
      projectId={ENV.VOUCH_PROJECT_ID}
      apiKey={ENV.VOUCH_CLIENT_KEY}
    >
      <SignupForm />
    </VouchProvider>
  );
}

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

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

    const result = await validate(email);

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

      if (allChecksPassed) {
        e.target.submit();
      }
    }
  };

  return <Form onSubmit={handleSubmit}>...</Form>;
}

Next Steps