Provider-agnostic CAPTCHA integration for the modern web.
Captigo is a TypeScript-first library ecosystem for integrating CAPTCHA providers. Instead of coupling your application to a specific provider's API, you get a single unified interface that works across Cloudflare Turnstile, hCaptcha, and Google reCAPTCHA — with first-class React and Vue 3 integrations.
Every provider has slightly different APIs, widget lifecycles, token shapes, and server-side verification flows. Switching providers — or A/B testing between them — means touching a lot of code. Captigo fixes that with a consistent abstraction that stays out of your way.
- One API, any provider. Swap Turnstile for hCaptcha without rewriting your integration.
- Framework-native. React and Vue integrations; optional
@captigo/nextjshelpers for route-handler verification. - Fully typed. Strict TypeScript throughout. No
any. No guessing. - Tree-shakeable. Import only what you use. Zero dead code in production bundles.
- Zero magic. No hidden globals, no monkey-patching, no surprises.
| Package | npm | Description |
|---|---|---|
@captigo/core |
Core types and provider adapter interface | |
@captigo/turnstile |
Cloudflare Turnstile adapter | |
@captigo/hcaptcha |
hCaptcha adapter | |
@captigo/recaptcha |
Google reCAPTCHA v2/v3 adapter | |
@captigo/react |
React 18+ hooks and components | |
@captigo/vue |
Vue 3 composables and components | |
@captigo/nextjs |
Next.js App Router helpers — request parsing, client IP, route-handler verification | |
@captigo/sveltekit |
— | SvelteKit integration (planned) |
The table below describes what each provider adapter actually supports today.
| Provider | Visible | Invisible | Passive | Reset | Destroy | Verify | Score | React | Vue | Example | Status |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Turnstile | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | stable |
| hCaptcha | ✅ | ✅ | — | ✅ | ✅ | ✅ | — | ✅ | ✅ | — | beta |
| reCAPTCHA v2 | ✅ | ✅ | — | ✅ | ✅ | — | ✅ | ✅ | — | beta | |
| reCAPTCHA v3 | — | — | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ | — | beta |
Column key:
- Visible — managed widget (visible checkbox, fires automatically)
- Invisible — interactive widget, challenge fires on explicit
widget.execute() - Passive — score-based, no user interaction or DOM widget (reCAPTCHA v3)
- Verify — server-side token verification helper included
- Score — verification result includes a risk score (
0.0–1.0)
Status key:
- stable — primary supported path; well-tested; actively maintained
- beta — functional and tested; receives less focused attention; edge cases may exist
⚠️ reCAPTCHA v2 destroy: the provider SDK has noremove()API, sowidget.destroy()callsreset()internally. The widget element is not removed from the DOM.
— reCAPTCHA v3 reset:
widget.reset()clears the cached token but is otherwise a no-op — there is no stateful widget to reset.
See docs/compatibility.md for detailed notes, known caveats, and provider recommendations.
pnpm add @captigo/core @captigo/turnstile @captigo/reactimport { turnstile } from "@captigo/turnstile";
import { Captcha } from "@captigo/react";
// Create the adapter once — outside the component to keep the widget stable.
const adapter = turnstile({ siteKey: "0x4AAAAAAAxxx" });
export function LoginForm() {
return (
<form onSubmit={handleSubmit}>
<Captcha adapter={adapter} onSuccess={(token) => setToken(token.value)} />
<button type="submit">Log in</button>
</form>
);
}On the server:
import { verifyToken } from "@captigo/turnstile";
const result = await verifyToken(token, process.env.TURNSTILE_SECRET!);
if (!result.success) return Response.json({ error: "CAPTCHA failed" }, { status: 400 });Swap turnstile(...) for hcaptcha(...) or recaptchaV2(...) — nothing else changes.
Every provider implements the CaptchaAdapter interface from @captigo/core. Your application code only ever depends on the interface, not the provider:
@captigo/core (types & contracts)
↑
│ implements CaptchaAdapter
├── @captigo/turnstile
├── @captigo/hcaptcha
└── @captigo/recaptcha
↑ consumes CaptchaAdapter
├── @captigo/react
├── @captigo/vue
└── @captigo/nextjs (server-side route handler helpers)
Configuring a different provider is a one-line change at the call site. Framework integration code is unchanged.
- Getting started — installation, quick starts for React and Vue
- Supported providers — Turnstile, hCaptcha, reCAPTCHA v2/v3 configuration
- Framework integrations — React hooks, Vue composables, invisible widgets
- Server-side verification — how to verify tokens safely
- Compatibility reference — feature matrix, per-provider caveats, and recommendations
- Architecture — design decisions and internal structure
Runnable examples — see examples/README.md for the full list. Highlights:
examples/react-signup-turnstile— signup form + Turnstile (client flow)examples/react-contact-server— contact form with Express +verifyTokenexamples/vue-form-turnstile— Vue contact form (optional shared API)examples/react-provider-swap— Turnstile vs hCaptcha, one adapter interfaceexamples/react-recaptcha-v3— passive score token (verify on server)examples/react-turnstile/examples/vue-turnstile— widget modesexamples/server-verify— minimal/verify/*routes for all providers
git clone https://github.com/moritzmyrz/captigo.git
cd captigo
pnpm install
pnpm build # build all packages
pnpm typecheck # type-check all packages
pnpm lint # run Biome linter
pnpm test # run all testsSee CONTRIBUTING.md for the full development guide.
Contributions are welcome. Please read CONTRIBUTING.md before opening a pull request, and review our Code of Conduct.
MIT — see LICENSE.