| name | widget-customization |
| description | Customize widget appearance, theme, execution mode, and behavior. Activate when styling the widget, changing when/how tokens are generated, or integrating with specific UX flows like multi-step forms.
|
| triggers | ["turnstile theme dark mode","turnstile invisible","turnstile execution mode","turnstile appearance","customize turnstile widget","turnstile callback not working","rerenderOnCallbackChange"] |
| category | core |
| metadata | {"library":"@marsidev/react-turnstile","library_version":"1.4.2","framework":"React"} |
Widget Customization
Customize the Turnstile widget's appearance, behavior, and integration with your application's UX.
Appearance Modes
Control when the widget is visible to users:
always (default)
Widget is always visible.
<Turnstile
siteKey="YOUR_SITE_KEY"
options={{ appearance: 'always' }}
/>
execute
Widget is hidden until you call execute() method. Useful for custom triggers.
import { Turnstile } from '@marsidev/react-turnstile'
import type { TurnstileInstance } from '@marsidev/react-turnstile'
import { useRef } from 'react'
export default function CustomTrigger() {
const ref = useRef<TurnstileInstance>(null)
return (
<>
<Turnstile
ref={ref}
siteKey="YOUR_SITE_KEY"
options={{
appearance: 'execute',
execution: 'execute'
}}
/>
<button onClick={() => ref.current?.execute()}>
Verify I'm Human
</button>
</>
)
}
interaction-only
Widget only appears when user interaction is required (most privacy-friendly).
<Turnstile
siteKey="YOUR_SITE_KEY"
options={{ appearance: 'interaction-only' }}
/>
Execution Modes
Control when the token is generated:
render (default)
Token is generated automatically when the widget renders.
execute
Token is generated only when you explicitly call execute(). Useful for delaying verification.
Themes
Match the widget to your application's color scheme:
<Turnstile siteKey="xxx" options={{ theme: 'auto' }} />
<Turnstile siteKey="xxx" options={{ theme: 'light' }} />
<Turnstile siteKey="xxx" options={{ theme: 'dark' }} />
Language/Localization
Set the widget language:
<Turnstile siteKey="xxx" options={{ language: 'auto' }} />
<Turnstile siteKey="xxx" options={{ language: 'en' }} />
<Turnstile siteKey="xxx" options={{ language: 'es' }} />
<Turnstile siteKey="xxx" options={{ language: 'de' }} />
See all supported languages: https://developers.cloudflare.com/turnstile/reference/supported-languages/
Dynamic Callbacks
By default, callbacks access the latest state without re-rendering the widget. For cases where you need the widget to re-render when callbacks change:
import { Turnstile } from '@marsidev/react-turnstile'
import { useCallback, useState } from 'react'
export default function DynamicForm() {
const [userType, setUserType] = useState<'user' | 'admin'>('user')
const handleSuccess = useCallback((token: string) => {
if (userType === 'admin') {
} else {
}
}, [userType])
return (
<Turnstile
siteKey="YOUR_SITE_KEY"
rerenderOnCallbackChange={true}
onSuccess={handleSuccess}
/>
)
}
⚠️ Warning: Without useCallback, this causes infinite re-renders!
Manual Script Injection
For better control over script loading (recommended for Next.js):
import {
Turnstile,
SCRIPT_URL,
DEFAULT_SCRIPT_ID
} from '@marsidev/react-turnstile'
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
id={DEFAULT_SCRIPT_ID}
src={SCRIPT_URL}
strategy="beforeInteractive"
/>
<Turnstile
siteKey="YOUR_SITE_KEY"
injectScript={false}
/>
</>
)
}
Custom Container Element
Change the HTML element used for the widget container:
<Turnstile siteKey="xxx" />
<Turnstile siteKey="xxx" as="span" />
<Turnstile siteKey="xxx" as="section" className="my-turnstile" />
Common Mistakes
❌ Forgetting useCallback with rerenderOnCallbackChange
Wrong:
<Turnstile
rerenderOnCallbackChange={true}
onSuccess={(token) => handleToken(token)}
/>
Correct:
const handleSuccess = useCallback((token: string) => {
handleToken(token)
}, [])
<Turnstile
rerenderOnCallbackChange={true}
onSuccess={handleSuccess}
/>
❌ Calling execute() Without Proper Setup
Wrong:
const ref = useRef<TurnstileInstance>(null)
<button onClick={() => ref.current?.execute()}>Verify</button>
<Turnstile ref={ref} siteKey="xxx" />
Correct:
const ref = useRef<TurnstileInstance>(null)
<button onClick={() => ref.current?.execute()}>Verify</button>
<Turnstile
ref={ref}
siteKey="xxx"
options={{
execution: 'execute',
appearance: 'execute'
}}
/>
See Also