JSPM

@naktor/contact-form

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 19
  • Score
    100M100P100Q74997F
  • License MIT

Reusable contact form with Turnstile CAPTCHA, Honeypot protection, and Zod validation

Package Exports

  • @naktor/contact-form
  • @naktor/contact-form/schemas
  • @naktor/contact-form/server

Readme

@naktor/contact-form

A reusable, customizable contact form component with built-in Cloudflare Turnstile CAPTCHA, honeypot protection, and Zod validation.

Features

  • Cloudflare Turnstile - Invisible CAPTCHA protection
  • Honeypot field - Additional bot protection
  • Zod validation - Type-safe form validation
  • i18n support - Built-in Spanish and English error messages
  • Render props - Fully customizable UI
  • TypeScript - Full type definitions included

Installation

npm install @naktor/contact-form
# or
pnpm add @naktor/contact-form
# or
yarn add @naktor/contact-form

Peer Dependencies

Make sure you have these installed:

npm install react react-dom zod

Usage

Client-side (React Component)

import { ContactForm, type ContactFormTranslations } from "@naktor/contact-form";
import { errorMessagesEN } from "@naktor/contact-form/schemas";

const translations: ContactFormTranslations = {
  labels: {
    name: "Name",
    company: "Company",
    email: "Email",
    message: "Message",
  },
  placeholders: {
    name: "Your name",
    email: "your@email.com",
    message: "Tell us about your project...",
  },
  submitButton: "Send Message",
  submittingButton: "Sending...",
  successTitle: "Message Sent!",
  successMessage: "We'll get back to you soon.",
  errorMessages: errorMessagesEN,
  apiErrors: {
    rateLimited: "Too many attempts. Please wait.",
    verificationFailed: "Verification failed. Please reload.",
    serverError: "Server error. Please try again.",
    networkError: "Connection error. Check your internet.",
  },
};

export default function ContactPage() {
  return (
    <ContactForm
      translations={translations}
      turnstileSiteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || ""}
      apiEndpoint="/api/contact"
      className="space-y-4"
      onSuccess={() => console.log("Form submitted!")}
    />
  );
}

Server-side (API Route)

// app/api/contact/route.ts (Next.js App Router)
import { verifyTurnstileToken, isHoneypotFilled } from "@naktor/contact-form/server";
import { contactSchemaServer } from "@naktor/contact-form/schemas";

export async function POST(request: Request) {
  const body = await request.json();

  // Check honeypot
  if (isHoneypotFilled(body.website)) {
    return Response.json({ success: true }); // Silent success for bots
  }

  // Verify Turnstile token
  const turnstileResult = await verifyTurnstileToken(body.turnstileToken);
  if (!turnstileResult.success) {
    return Response.json({ error: "Verification failed" }, { status: 403 });
  }

  // Validate form data
  const validationResult = contactSchemaServer.safeParse(body);
  if (!validationResult.success) {
    return Response.json({ error: "Invalid data" }, { status: 400 });
  }

  // Process the form (send email, save to DB, etc.)
  const { name, email, company, message } = validationResult.data;

  // ... your logic here

  return Response.json({ success: true });
}

Customizing the UI

Use render props to fully customize the form appearance:

<ContactForm
  translations={translations}
  turnstileSiteKey="..."
  renderInput={({ id, name, type, label, placeholder, value, onChange, error }) => (
    <div className="my-custom-input">
      <label htmlFor={id}>{label}</label>
      <input
        id={id}
        name={name}
        type={type}
        placeholder={placeholder}
        value={value}
        onChange={onChange}
      />
      {error && <span className="error">{error}</span>}
    </div>
  )}
  renderButton={({ type, disabled, isSubmitting, text }) => (
    <button type={type} disabled={disabled} className="my-button">
      {text}
    </button>
  )}
  renderSuccess={({ title, message }) => (
    <div className="success-message">
      <h3>{title}</h3>
      <p>{message}</p>
    </div>
  )}
/>

Environment Variables

# Client-side (public)
NEXT_PUBLIC_TURNSTILE_SITE_KEY=your_site_key

# Server-side (secret)
TURNSTILE_SECRET_KEY=your_secret_key

API Reference

Exports

@naktor/contact-form (client)

  • ContactForm - Main form component
  • TurnstileWidget - Standalone Turnstile widget
  • resetTurnstile - Function to reset the widget

@naktor/contact-form/server

  • verifyTurnstileToken - Verify Turnstile token on server
  • isHoneypotFilled - Check if honeypot field was filled

@naktor/contact-form/schemas

  • createContactSchema - Create a Zod schema with custom messages
  • contactSchemaServer - Pre-configured schema for server
  • errorMessagesES - Spanish error messages
  • errorMessagesEN - English error messages

License

MIT