@letar/forms

CAPTCHA

Bot protection with Cloudflare Turnstile, Google reCAPTCHA, or hCaptcha

Overview

Form.Captcha adds bot protection to forms. Supports three providers: Cloudflare Turnstile (recommended, free), Google reCAPTCHA, and hCaptcha.

Setup

Configure CAPTCHA once in createForm:

const AppForm = createForm({
  captcha: {
    provider: 'turnstile',
    siteKey: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY!,
    theme: 'auto',
  },
})

Usage in Form

<AppForm onSubmit={handleSubmit}>
  <AppForm.Field.String name="email" label="Email" />
  <AppForm.Field.Textarea name="message" label="Message" />
  <AppForm.Captcha />
  <AppForm.Button.Submit>Send</AppForm.Button.Submit>
</AppForm>

The CAPTCHA token is automatically included in the form data as __captchaToken.

Server Verification

import { verifyCaptcha } from '@letar/forms/captcha'

export async function submitAction(data: FormData) {
  const token = data.__captchaToken
  const result = await verifyCaptcha(token, {
    provider: 'turnstile',
    secretKey: process.env.TURNSTILE_SECRET_KEY!,
  })

  if (!result.success) {
    throw new Error('CAPTCHA verification failed')
  }
  // Process form...
}

Providers

ProviderFreeInvisibleNotes
TurnstileYesYesRecommended, privacy-friendly
reCAPTCHAPartialv3 onlyGoogle, most popular
hCaptchaPartialYesPrivacy alternative to reCAPTCHA

Props

PropTypeDefaultDescription
provider'turnstile' | 'recaptcha' | 'hcaptcha'from createFormOverride provider
siteKeystringfrom createFormOverride siteKey
theme'auto' | 'light' | 'dark''auto'Widget theme
size'normal' | 'compact' | 'invisible''normal'Widget size
languagestringLanguage code (ISO 639-1)
onSuccess(token: string) => voidSuccess callback
onError(error) => voidError callback
onExpire() => voidToken expiry callback

Live Example

Try the interactive example on forms-example.letar.best.

On this page