React

React integration for NeoCaptcha.

Updated 2/22/2026

React Integration

Use this guide for React, Next.js, Vite, Remix, and similar frameworks.

Install

npm install @neocaptcha/sdk

<NeoCaptcha> component

'use client';
 
import { useState } from 'react';
import { NeoCaptcha } from '@neocaptcha/sdk/react';
 
export default function SignupForm() {
  const [token, setToken] = useState<string | null>(null);
 
  return (
    <form
      onSubmit={async (e) => {
        e.preventDefault();
        if (!token) return;
        await fetch('/api/signup', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ captchaToken: token }),
        });
      }}
    >
      {/* other fields */}
      <NeoCaptcha
        siteKey={process.env.NEXT_PUBLIC_NEOCAPTCHA_SITE_KEY!}
        onComplete={(verifyToken) => setToken(verifyToken)}
        onError={() => setToken(null)}
        onExpire={() => setToken(null)}
      />
      <button type="submit" disabled={!token}>
        Submit
      </button>
    </form>
  );
}

Props

PropTypeDescription
siteKeystringRequired. Your site key.
expiryTimeoutnumber | nullOptional. Token expiry in ms.
onLoad() => voidWidget iframe is ready.
onComplete(token: string) => voidUser passed the challenge.
onError() => voidError occcured.
onExpire() => voidToken expired.

Next.js guidance

  • Render the widget only in client components ('use client').
  • Use NEXT_PUBLIC_NEOCAPTCHA_SITE_KEY for the site key (public, safe for frontend).
  • Keep NEOCAPTCHA_SITE_SECRET server-only — never prefix it with NEXT_PUBLIC_.
  • Pass the token to API routes or server actions, then verify server-side.

Common mistakes

  • Submitting a form before token is set (before onComplete fires).
  • Treating a completed token as final trust without backend verification.