hCaptcha Integration

Protect your forms with hCaptcha's privacy-focused CAPTCHA service.

Overview

hCaptcha is a privacy-focused CAPTCHA service that provides strong spam protection while respecting user privacy. Unlike other CAPTCHA services, hCaptcha is designed to be GDPR/CCPA compliant and offers better performance with lower user friction.

✨ hCaptcha Benefits

  • GDPR/CCPA compliant by design
  • Lower user friction than traditional CAPTCHAs
  • Better accessibility features
  • No data tracking or behavioral analysis
  • Enterprise-grade security and reliability

Setup

1. Get hCaptcha Keys

First, create an account at hCaptcha.com and register your domain to get your site key and secret key.

You'll receive:

  • Site Key: Used in your HTML/JavaScript
  • Secret Key: Used for server-side verification (keep secret!)

2. Configure in Kitoform Dashboard

In your Kitoform dashboard, go to your form settings and configure hCaptcha:

{
  "captchaProvider": "hcaptcha",
  "hcaptchaSiteKey": "your-site-key-here",
  "hcaptchaSecretKey": "your-secret-key-here",
  "securityLevel": "standard"
}

HTML Implementation

Basic Implementation

Add the hCaptcha widget to your HTML form:

<!DOCTYPE html>
<html>
<head>
  <title>Contact Form with hCaptcha</title>
  <script src="https://js.hcaptcha.com/1/api.js" async defer></script>
</head>
<body>
  <form action="https://connect.kitoform.com/f/YOUR_ENDPOINT_ID" method="POST">
    <div>
      <label for="name">Name:</label>
      <input type="text" name="name" id="name" required>
    </div>
    
    <div>
      <label for="email">Email:</label>
      <input type="email" name="email" id="email" required>
    </div>
    
    <div>
      <label for="message">Message:</label>
      <textarea name="message" id="message" required></textarea>
    </div>
    
    <!-- hCaptcha widget -->
    <div class="h-captcha" data-sitekey="YOUR_SITE_KEY"></div>
    
    <button type="submit">Send Message</button>
  </form>
</body>
</html>

With JavaScript Callbacks

Handle success, error, and expiration events:

<script>
  // Callback when hCaptcha is solved
  function onHcaptchaSuccess(token) {
    console.log('hCaptcha solved, token:', token);
    
    // Enable submit button
    document.getElementById('submit-btn').disabled = false;
    
    // Optional: Submit form automatically
    // document.getElementById('contact-form').submit();
  }
  
  // Callback when hCaptcha expires
  function onHcaptchaExpired() {
    console.log('hCaptcha token expired');
    
    // Disable submit button
    document.getElementById('submit-btn').disabled = true;
  }
  
  // Callback when hCaptcha encounters an error
  function onHcaptchaError(error) {
    console.error('hCaptcha error:', error);
    
    // Disable submit button
    document.getElementById('submit-btn').disabled = true;
  }
</script>

<form id="contact-form" action="https://connect.kitoform.com/f/YOUR_ENDPOINT_ID" method="POST">
  <!-- Form fields here -->
  
  <div class="h-captcha" 
       data-sitekey="YOUR_SITE_KEY"
       data-callback="onHcaptchaSuccess"
       data-expired-callback="onHcaptchaExpired"
       data-error-callback="onHcaptchaError">
  </div>
  
  <button type="submit" id="submit-btn" disabled>Send Message</button>
</form>

React Implementation

Installation

Install the React hCaptcha component:

npm install @hcaptcha/react-hcaptcha

Basic React Component

import React, { useState, useRef } from 'react';
import HCaptcha from '@hcaptcha/react-hcaptcha';

function ContactForm() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: ''
  });
  const [captchaToken, setCaptchaToken] = useState('');
  const [isSubmitting, setIsSubmitting] = useState(false);
  const captchaRef = useRef(null);

  const handleInputChange = (e) => {
    setFormData({
      ...formData,
      [e.target.name]: e.target.value
    });
  };

  const handleCaptchaVerify = (token) => {
    console.log('hCaptcha verified:', token);
    setCaptchaToken(token);
  };

  const handleCaptchaExpire = () => {
    console.log('hCaptcha expired');
    setCaptchaToken('');
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    
    if (!captchaToken) {
      alert('Please complete the CAPTCHA');
      return;
    }

    setIsSubmitting(true);

    try {
      const formDataWithCaptcha = new FormData();
      Object.keys(formData).forEach(key => {
        formDataWithCaptcha.append(key, formData[key]);
      });
      formDataWithCaptcha.append('h-captcha-response', captchaToken);

      const response = await fetch('https://connect.kitoform.com/f/YOUR_ENDPOINT_ID', {
        method: 'POST',
        body: formDataWithCaptcha,
      });

      if (response.ok) {
        alert('Form submitted successfully!');
        // Reset form
        setFormData({ name: '', email: '', message: '' });
        setCaptchaToken('');
        captchaRef.current?.resetCaptcha();
      } else {
        alert('Form submission failed. Please try again.');
      }
    } catch (error) {
      console.error('Submission error:', error);
      alert('Form submission failed. Please try again.');
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="name">Name:</label>
        <input
          type="text"
          name="name"
          id="name"
          value={formData.name}
          onChange={handleInputChange}
          required
        />
      </div>

      <div>
        <label htmlFor="email">Email:</label>
        <input
          type="email"
          name="email"
          id="email"
          value={formData.email}
          onChange={handleInputChange}
          required
        />
      </div>

      <div>
        <label htmlFor="message">Message:</label>
        <textarea
          name="message"
          id="message"
          value={formData.message}
          onChange={handleInputChange}
          required
        />
      </div>

      <HCaptcha
        ref={captchaRef}
        sitekey="YOUR_SITE_KEY"
        onVerify={handleCaptchaVerify}
        onExpire={handleCaptchaExpire}
      />

      <button 
        type="submit" 
        disabled={!captchaToken || isSubmitting}
      >
        {isSubmitting ? 'Sending...' : 'Send Message'}
      </button>
    </form>
  );
}

export default ContactForm;

Configuration Options

Widget Customization

Customize the hCaptcha widget appearance and behavior:

<!-- Size variations -->
<div class="h-captcha" 
     data-sitekey="YOUR_SITE_KEY"
     data-size="normal">     <!-- normal, compact, invisible -->
</div>

<!-- Theme options -->
<div class="h-captcha" 
     data-sitekey="YOUR_SITE_KEY"
     data-theme="light">     <!-- light, dark -->
</div>

<!-- Language setting -->
<div class="h-captcha" 
     data-sitekey="YOUR_SITE_KEY"
     data-hl="es">          <!-- Language code (es, en, fr, etc.) -->
</div>

<!-- Tab index for accessibility -->
<div class="h-captcha" 
     data-sitekey="YOUR_SITE_KEY"
     data-tabindex="0">
</div>

<!-- Complete customization example -->
<div class="h-captcha" 
     data-sitekey="YOUR_SITE_KEY"
     data-size="compact"
     data-theme="dark"
     data-hl="en"
     data-tabindex="0"
     data-callback="onSuccess"
     data-expired-callback="onExpired"
     data-error-callback="onError">
</div>

React Component Props

<HCaptcha
  sitekey="YOUR_SITE_KEY"
  size="normal"              // normal, compact, invisible
  theme="light"              // light, dark
  tabindex={0}               // Tab index for accessibility
  languageOverride="en"      // Language override
  reCaptchaCompat={false}    // reCAPTCHA compatibility mode
  onVerify={handleVerify}    // Called when solved
  onExpire={handleExpire}    // Called when expired
  onError={handleError}      // Called on error
  onLoad={handleLoad}        // Called when loaded
/>

Security Levels

Configure different security levels in your Kitoform dashboard to balance security and user experience.

🟢 Standard Security

Balanced security with good user experience. Recommended for most use cases.

  • • Standard hCaptcha difficulty
  • • Automatic retry on failure
  • • Good for contact forms and newsletters

🟡 Strict Security

Higher security with additional validation. Use for important forms.

  • • Enhanced difficulty settings
  • • Additional server-side validation
  • • Good for registrations and lead forms

🔴 Paranoid Security

Maximum security with additional checks. Use for critical forms only.

  • • Highest difficulty settings
  • • Rate limiting and IP checks
  • • Additional honeypot fields
  • • Good for payment and sensitive forms

Troubleshooting

Common Issues

hCaptcha widget not loading

Make sure the hCaptcha script is loaded before rendering the widget:

<script src="https://js.hcaptcha.com/1/api.js" async defer></script>

Invalid site key error

Verify your site key matches your domain configuration in the hCaptcha dashboard.

Form submission failing

Ensure the hCaptcha response field is included in your form data:

formData.append('h-captcha-response', captchaToken);

Testing

hCaptcha provides test keys for development:

Development keys (always pass):

Site Key: 10000000-ffff-ffff-ffff-000000000001

Secret Key: 0x0000000000000000000000000000000000000000

⚠️ These keys always pass validation - use only for testing!

Best Practices

✅ Do's

  • Use test keys during development and staging
  • Handle expired tokens gracefully with auto-refresh
  • Provide clear feedback to users when CAPTCHA is required
  • Set appropriate tab indexes for keyboard navigation

❌ Don'ts

  • Don't use production keys in client-side code repositories
  • Don't submit forms without validating CAPTCHA completion
  • Don't rely solely on client-side CAPTCHA validation

Related Documentation