Cloudflare Turnstile integration for Next.js applications
npm install next-turnstileA type-safe, feature-rich integration of Cloudflare Turnstile for Next.js applications. This package provides both client and server-side components for seamless CAPTCHA integration.


- ๐ Type-safe: Full TypeScript support
- ๐จ Customizable: Extensive styling and behavior options
- ๐งช Sandbox Mode: Built-in support for development testing
- โก Server Validation: Easy token verification
- ๐ฑ Responsive: Works across all device sizes
- ๐ Auto-reload: Configurable token refresh
- ๐ Theme Support: Light, dark, and auto themes
- ๐ i18n Ready: Multiple language support
``bashnpm
npm install next-turnstile
Quick Start
$3
`tsx
import { Turnstile } from "next-turnstile";function MyForm() {
const handleVerify = (token: string) => {
// Handle the verification token
console.log("Verification successful:", token);
};
return (
);
}
`$3
`tsx
import { validateTurnstileToken } from "next-turnstile";async function validateToken(token: string) {
try {
const result = await validateTurnstileToken({
token,
secretKey: process.env.TURNSTILE_SECRET_KEY,
});
if (result.success) {
// Token is valid
return true;
}
} catch (error) {
console.error("Validation failed:", error);
}
return false;
}
`API Reference
$3
| Prop | Type | Default | Description |
| --------------------- | ----------------------------------------------------------------------- | ------------------------- | --------------------------------------------- |
|
siteKey | string | Required | Your Cloudflare Turnstile site key |
| onVerify | (token: string) => void | - | Callback when verification succeeds |
| onError | (error: unknown) => void | - | Callback when an error occurs |
| onExpire | () => void | - | Callback when the token expires |
| onLoad | () => void | - | Callback when the widget loads |
| onBeforeInteractive | () => void | - | Callback before challenge enters interactive |
| onAfterInteractive | () => void | - | Callback after challenge leaves interactive |
| onUnsupported | () => void | - | Callback when browser is not supported |
| onTimeout | () => void | - | Callback when interactive challenge times out |
| action | string | - | Custom action value for analytics |
| theme | 'light' \| 'dark' \| 'auto' | 'auto' | Widget theme |
| size | 'normal' \| 'compact' \| 'flexible' | 'flexible' | Widget size |
| appearance | 'always' \| 'execute' \| 'interaction-only' | 'always' | When to show the widget |
| execution | 'render' \| 'execute' | 'render' | When to execute the challenge |
| retry | 'auto' \| 'never' | 'auto' | Retry behavior on failure |
| retryInterval | number | 8000 | Milliseconds between retries |
| refreshExpired | 'auto' \| 'manual' \| 'never' | 'auto' | Token refresh behavior |
| refreshTimeout | 'auto' \| 'manual' \| 'never' | 'auto' | Timeout refresh behavior |
| language | string | - | Widget language code |
| tabIndex | number | - | Tab index for accessibility |
| responseField | boolean | true | Create hidden response input |
| responseFieldName | string | 'cf-turnstile-response' | Name of the response input field |
| cData | string | - | Custom data payload |
| feedbackEnabled | boolean | true | Enable visitor feedback collection |
| id | string | 'turnstile-widget' | Container element ID |
| className | string | - | Additional CSS classes |
| sandbox | boolean \| pass \| block \| pass-invisible \| block-invisible | false | Enable sandbox mode |$3
| Option | Type | Required | Description |
| ---------------- | --------------------------------------- | -------- | ------------------------- |
|
token | string | Yes | The token from the client |
| secretKey | string | Yes | Your Turnstile secret key |
| remoteip | string | No | User's IP address |
| idempotencyKey | string | No | Unique request identifier |
| sandbox | boolean\| pass \| fail \| error | No | Enable sandbox mode |Advanced Usage
$3
`tsx
import { Turnstile } from "next-turnstile";export default function Form() {
const [token, setToken] = useState();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!token) return;
const response = await fetch("/api/submit", {
method: "POST",
body: JSON.stringify({ token }),
headers: { "Content-Type": "application/json" },
});
// Handle response...
};
return (
);
}
`$3
`tsx
import { validateTurnstileToken } from "next-turnstile";async function submitForm(formData: FormData) {
"use server";
const token = formData.get("cf-turnstile-response");
if (!token || typeof token !== "string") {
return { error: "No token provided" };
}
const result = await validateTurnstileToken({
token,
secretKey: process.env.TURNSTILE_SECRET_KEY!,
});
if (!result.success) {
return { error: "Invalid token" };
}
// Process form submission...
}
`$3
During development, you can use sandbox mode to test without real credentials:
`tsx
siteKey="1x00000000000000000000AA"
sandbox={process.env.NODE_ENV === "development"}
onVerify={handleVerify}
/>
`$3
`tsx
siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY!}
theme="dark"
onVerify={handleVerify}
/>
`$3
`tsx
siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY!}
className="my-turnstile-widget"
onVerify={handleVerify}
/>
`Development and Contributing
1. Clone the repository
2. Install dependencies:
`bash
pnpm install
`
3. Start development:
`bash
pnpm dev
``MIT ยฉ Jed Patterson
Built with โค๏ธ using: