Documentation Index Fetch the complete documentation index at: https://mintlify.com/marsidev/react-turnstile/llms.txt
Use this file to discover all available pages before exploring further.
Get started with React Turnstile by following these simple steps. You’ll have a working Turnstile widget in your React application in just a few minutes.
Obtain Cloudflare Turnstile Keys
Before you can use React Turnstile, you need to obtain a sitekey and secret key from Cloudflare.
Go to the Cloudflare Dashboard
Navigate to Turnstile in the sidebar
Click Add Site to create a new widget
Configure your widget settings and domain
Save your sitekey (for client-side) and secret key (for server-side validation)
For development and testing, you can use Cloudflare’s test keys:
Sitekey (always passes) : 1x00000000000000000000AA
Secret key (always passes) : 1x0000000000000000000000000000000AA
Install React Turnstile
Install the package using your preferred package manager: npm install @marsidev/react-turnstile
Add Turnstile to Your Component
Import and use the Turnstile component in your React application: import { Turnstile } from '@marsidev/react-turnstile'
function App () {
return (
< div >
< h1 > Contact Form </ h1 >
< form >
< input type = "email" placeholder = "Email" required />
< textarea placeholder = "Message" required />
{ /* Add Turnstile widget */ }
< Turnstile siteKey = "1x00000000000000000000AA" />
< button type = "submit" > Submit </ button >
</ form >
</ div >
)
}
export default App
Replace 1x00000000000000000000AA with your actual Cloudflare Turnstile sitekey.
Handle the Verification Token
Capture the token when the user completes the challenge: import { Turnstile } from '@marsidev/react-turnstile'
import { useState } from 'react'
function App () {
const [ token , setToken ] = useState < string >()
const handleSubmit = ( e : React . FormEvent ) => {
e . preventDefault ()
if ( ! token ) {
alert ( 'Please complete the verification' )
return
}
console . log ( 'Verification token:' , token )
// Send token to your server for validation
}
return (
< form onSubmit = { handleSubmit } >
< input type = "email" placeholder = "Email" required />
< textarea placeholder = "Message" required />
< Turnstile
siteKey = "1x00000000000000000000AA"
onSuccess = { ( token ) => {
console . log ( 'Success! Token:' , token )
setToken ( token )
} }
onError = { () => setToken ( undefined ) }
onExpire = { () => setToken ( undefined ) }
/>
< button type = "submit" disabled = { ! token } >
Submit
</ button >
</ form >
)
}
Validate Token on Your Server
Always validate the token on your server. Never trust client-side verification alone.
Send the token to your backend and verify it with Cloudflare: async function verifyTurnstileToken ( token : string ) {
const response = await fetch (
'https://challenges.cloudflare.com/turnstile/v0/siteverify' ,
{
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
secret: process . env . TURNSTILE_SECRET_KEY ,
response: token
})
}
)
const data = await response . json ()
return data . success
}
// Example API endpoint
app . post ( '/api/submit' , async ( req , res ) => {
const { token } = req . body
const isValid = await verifyTurnstileToken ( token )
if ( isValid ) {
// Process the form submission
res . json ({ success: true })
} else {
res . status ( 400 ). json ({ error: 'Verification failed' })
}
})
Store your Turnstile secret key in environment variables: TURNSTILE_SECRET_KEY = 1x0000000000000000000000000000000AA
What’s Next?
You now have a working Turnstile widget! Here are some common next steps:
Component Props Explore all available component properties
Widget Lifecycle Understand the widget lifecycle and callbacks
Interact with Widget Control the widget programmatically using refs
Server Validation Learn how to validate tokens on your server
Common Customizations
Theme
Match the widget to your site’s appearance:
< Turnstile
siteKey = "1x00000000000000000000AA"
options = { {
theme: 'dark' // or 'light', 'auto'
} }
/>
Size
Control the widget dimensions:
< Turnstile
siteKey = "1x00000000000000000000AA"
options = { {
size: 'compact' // or 'normal', 'flexible'
} }
/>
Invisible Mode
For a frictionless user experience:
import { Turnstile , TurnstileInstance } from '@marsidev/react-turnstile'
import { useRef } from 'react'
function InvisibleForm () {
const turnstileRef = useRef < TurnstileInstance >( null )
const handleSubmit = async () => {
// Trigger the invisible challenge
turnstileRef . current ?. execute ()
// Wait for the token
const token = await turnstileRef . current ?. getResponsePromise ()
console . log ( 'Token:' , token )
}
return (
< div >
< Turnstile
ref = { turnstileRef }
siteKey = "1x00000000000000000000AA"
options = { {
execution: 'execute' ,
size: 'invisible'
} }
/>
< button onClick = { handleSubmit } > Submit </ button >
</ div >
)
}
Need Help?
Troubleshooting Common issues and solutions
Examples View real-world examples
GitHub Report issues or contribute
Cloudflare Docs Official Turnstile documentation