Skip to main content
The <Turnstile /> component accepts a variety of props to customize its behavior, appearance, and lifecycle callbacks. This page documents all available props extracted from the source code.

Required Props

siteKey
string
required
Your Cloudflare Turnstile sitekey. This sitekey is associated with the corresponding widget configuration and is created upon the widget creation.
<Turnstile siteKey="1x00000000000000000000AA" />

Callback Props

Callbacks are invoked at different stages of the widget lifecycle.
onSuccess
(token: string) => void
Callback invoked upon success of the challenge. The callback is passed a token that can be validated on your server.
<Turnstile
  siteKey="your-site-key"
  onSuccess={(token) => {
    console.log('Challenge successful:', token)
    // Submit token to your backend for verification
  }}
/>
onError
(error: string) => void
Callback invoked when there is an error (e.g., network error or the challenge failed). The callback is passed an error code.See Client-side errors for error codes.
<Turnstile
  siteKey="your-site-key"
  onError={(error) => {
    console.error('Turnstile error:', error)
  }}
/>
onExpire
(token: string) => void
Callback invoked when a challenge expires. Tokens expire after a certain duration and need to be refreshed.
<Turnstile
  siteKey="your-site-key"
  onExpire={(token) => {
    console.log('Token expired:', token)
    // Prompt user to refresh or auto-refresh
  }}
/>
onTimeout
() => void
Callback invoked when the Turnstile widget times out.
<Turnstile
  siteKey="your-site-key"
  onTimeout={() => {
    console.warn('Widget timed out')
  }}
/>
onBeforeInteractive
() => void
Callback invoked before the user is prompted for interactivity. Useful for showing loading states.
<Turnstile
  siteKey="your-site-key"
  onBeforeInteractive={() => {
    console.log('About to show interactive challenge')
  }}
/>
onAfterInteractive
() => void
Callback invoked when the interactive challenge has been solved.
<Turnstile
  siteKey="your-site-key"
  onAfterInteractive={() => {
    console.log('Interactive challenge completed')
  }}
/>
onUnsupported
() => void
Callback invoked when the browser is not supported by Turnstile.
<Turnstile
  siteKey="your-site-key"
  onUnsupported={() => {
    alert('Your browser is not supported. Please use a modern browser.')
  }}
/>
onWidgetLoad
(widgetId: string) => void
Callback invoked after a successful render of the widget. The callback is passed the widget ID. It does not trigger when the widget is reset.
<Turnstile
  siteKey="your-site-key"
  onWidgetLoad={(widgetId) => {
    console.log('Widget loaded with ID:', widgetId)
  }}
/>

Widget Options

The options prop accepts a ComponentRenderOptions object to configure the widget’s appearance and behavior.
options
ComponentRenderOptions
Custom widget render options. See individual properties below.

Appearance Options

options.theme
'auto' | 'light' | 'dark'
default:"auto"
The widget theme. The default is "auto", which respects the user preference.
<Turnstile
  siteKey="your-site-key"
  options={{ theme: 'dark' }}
/>
options.size
'normal' | 'compact' | 'flexible' | 'invisible'
default:"normal"
The size of the Turnstile widget:
  • normal: 300x65px
  • compact: 150x140px
  • flexible: 100% width (min: 300px) x 65px
  • invisible: No widget shown (only for invisible type widgets)
<Turnstile
  siteKey="your-site-key"
  options={{ size: 'compact' }}
/>
options.appearance
'always' | 'execute' | 'interaction-only'
default:"always"
The appearance mode of the Turnstile widget:
  • always: Widget is always visible
  • execute: Widget only appears when executing
  • interaction-only: Widget only shown when/if interactivity is required
<Turnstile
  siteKey="your-site-key"
  options={{ appearance: 'interaction-only' }}
/>
options.language
string
default:"auto"
The language for the widget. Must be a valid ISO 639-1 country code, or "auto" to detect automatically.Supported languages include: 'en', 'es', 'fr', 'de', 'ja', 'zh', and many more.
<Turnstile
  siteKey="your-site-key"
  options={{ language: 'es' }}
/>

Behavior Options

options.execution
'render' | 'execute'
default:"render"
Execution controls when to obtain the token of the widget:
  • render: Token obtained automatically on render (default)
  • execute: Token obtained only when .execute() is called
const turnstileRef = useRef<TurnstileInstance>(null)

<Turnstile
  ref={turnstileRef}
  siteKey="your-site-key"
  options={{ execution: 'execute' }}
/>

// Later, manually trigger execution
turnstileRef.current?.execute()
options.retry
'auto' | 'never'
default:"auto"
How to retry on widget failure:
  • auto: Allows the user to retry
  • never: No retry option
<Turnstile
  siteKey="your-site-key"
  options={{ retry: 'auto' }}
/>
options.retryInterval
number
default:8000
Duration in milliseconds before the widget automatically retries.
<Turnstile
  siteKey="your-site-key"
  options={{ retryInterval: 5000 }} // 5 seconds
/>
options.refreshExpired
'auto' | 'manual' | 'never'
default:"auto"
The refresh mode to use when the given Turnstile token expires:
  • auto: Automatically refreshes the widget
  • manual: Prompts the user with a refresh button
  • never: Never refreshes the widget
<Turnstile
  siteKey="your-site-key"
  options={{ refreshExpired: 'manual' }}
/>
options.refreshTimeout
'auto' | 'manual' | 'never'
default:"auto"
The refresh mode to use when the widget times out:
  • auto: Automatically refreshes the widget
  • manual: Prompts the user with a refresh button
  • never: Never refreshes the widget
<Turnstile
  siteKey="your-site-key"
  options={{ refreshTimeout: 'auto' }}
/>

Data Options

options.action
string
A customer value that can be used to differentiate widgets under the same sitekey in analytics and which is returned upon validation.
<Turnstile
  siteKey="your-site-key"
  options={{ action: 'login' }}
/>
options.cData
string
A customer payload that can be used to attach customer data to the challenge throughout its issuance and which is returned upon validation.
<Turnstile
  siteKey="your-site-key"
  options={{ cData: JSON.stringify({ userId: '123' }) }}
/>

Accessibility & Form Options

options.tabIndex
number
default:0
The tabindex of Turnstile’s iframe for accessibility purposes.
<Turnstile
  siteKey="your-site-key"
  options={{ tabIndex: 1 }}
/>
options.responseField
boolean
default:true
Whether to add or not a hidden response input element with the turnstile token. Useful for form submissions.
<Turnstile
  siteKey="your-site-key"
  options={{ responseField: true }}
/>
options.responseFieldName
string
default:"cf-turnstile-response"
The name of the hidden input element added to the container where Turnstile is injected.
<Turnstile
  siteKey="your-site-key"
  options={{ responseFieldName: 'turnstile-token' }}
/>
options.feedbackEnabled
boolean
default:true
Allows Cloudflare to gather visitor feedback upon widget failure.
<Turnstile
  siteKey="your-site-key"
  options={{ feedbackEnabled: false }}
/>

Script Control Props

injectScript
boolean
default:true
Controls if the script is automatically injected or not. If you want to inject the script manually, set this property to false.See Script Injection for more details.
<Turnstile
  siteKey="your-site-key"
  injectScript={false}
/>
onLoadScript
() => void
Callback invoked when the Turnstile script is loaded.
<Turnstile
  siteKey="your-site-key"
  onLoadScript={() => {
    console.log('Turnstile script loaded')
  }}
/>
scriptOptions
ScriptOptions
Custom injected script options. See Script Injection for details.

Advanced Props

as
React.ElementType
default:"div"
Define the HTML tag of the widget container.
<Turnstile
  siteKey="your-site-key"
  as="section"
/>
rerenderOnCallbackChange
boolean
default:false
Controls whether the widget re-renders when callback props change.
  • false (default): Stable callbacks - better performance, callbacks don’t cause widget re-renders
  • true: Dynamic callbacks - widget re-renders when callbacks change, useful for intentional callback updates
Important: When set to true, wrap your callback functions with useCallback to prevent unnecessary widget re-renders on every parent component re-render.
const handleSuccess = useCallback((token: string) => {
  console.log('Success:', token)
}, [])

<Turnstile
  siteKey="your-site-key"
  rerenderOnCallbackChange={true}
  onSuccess={handleSuccess}
/>

HTML Attributes

The component extends React.HTMLAttributes<HTMLDivElement>, so you can pass any standard HTML attributes (except onError, which is reserved for the Turnstile callback):
<Turnstile
  siteKey="your-site-key"
  id="my-turnstile"
  className="custom-class"
  style={{ margin: '20px' }}
  data-testid="turnstile-widget"
/>

Common Prop Combinations

Perfect for form submissions with hidden field:
<form onSubmit={handleSubmit}>
  <Turnstile
    siteKey="your-site-key"
    options={{
      responseField: true,
      responseFieldName: 'cf-turnstile-response',
      size: 'normal'
    }}
    onSuccess={(token) => {
      // Token automatically added to form
      console.log('Token ready:', token)
    }}
  />
  <button type="submit">Submit</button>
</form>
No UI, programmatic execution:
const turnstileRef = useRef<TurnstileInstance>(null)

<Turnstile
  ref={turnstileRef}
  siteKey="your-site-key"
  options={{
    size: 'invisible',
    execution: 'execute'
  }}
  onSuccess={(token) => {
    // Submit token to backend
  }}
/>

// Trigger when needed
<button onClick={() => turnstileRef.current?.execute()}>
  Verify
</button>
Custom appearance with manual token refresh:
<Turnstile
  siteKey="your-site-key"
  options={{
    theme: 'dark',
    size: 'compact',
    refreshExpired: 'manual',
    language: 'en'
  }}
  onExpire={() => {
    console.log('Token expired - user must refresh')
  }}
/>
Comprehensive error and timeout handling:
<Turnstile
  siteKey="your-site-key"
  onSuccess={(token) => {
    console.log('Success:', token)
  }}
  onError={(error) => {
    console.error('Error:', error)
    // Handle error based on error code
  }}
  onTimeout={() => {
    console.warn('Widget timed out')
  }}
  onUnsupported={() => {
    console.error('Browser not supported')
  }}
  options={{
    retry: 'auto',
    retryInterval: 3000
  }}
/>

Type Definitions

For TypeScript users, all prop types are exported from the package:
import type { 
  TurnstileProps, 
  ComponentRenderOptions,
  ScriptOptions 
} from '@marsidev/react-turnstile'
View the complete type definitions in types.ts:156.

Build docs developers (and LLMs) love