Skip to main content

Interface

interface TurnstileInstance extends Omit<Turnstile.Turnstile, 'ready'> {
  render: () => string | null | undefined
  execute: () => void
  reset: () => void
  remove: () => void
  getResponse: () => string | undefined
  getResponsePromise: (timeout?: number, retry?: number) => Promise<string>
  isExpired: () => boolean
}

Usage

Access these methods using React refs:
import { useRef } from 'react'
import Turnstile from '@marsidev/react-turnstile'
import type { TurnstileInstance } from '@marsidev/react-turnstile'

function MyComponent() {
  const turnstileRef = useRef<TurnstileInstance>(null)

  const handleReset = () => {
    turnstileRef.current?.reset()
  }

  return <Turnstile ref={turnstileRef} siteKey="your-site-key" />
}

Methods

render

render(): string | null | undefined
Explicitly renders the Turnstile widget. Returns: The rendered widget ID, or undefined if rendering fails. Example:
const widgetId = turnstileRef.current?.render()
if (widgetId) {
  console.log('Widget rendered with ID:', widgetId)
}

execute

execute(): void
Renders a widget when options.execution is set to 'execute'. This method should be called after the .render() method. If options.execution is set to 'render', this method has no effect. Example:
// First render the widget
turnstileRef.current?.render()

// Then execute when ready (for invisible widgets)
turnstileRef.current?.execute()

reset

reset(): void
Resets the Turnstile widget to its initial state. Useful for allowing users to retry after an error or when form submission fails. Example:
const handleFormError = () => {
  // Reset the widget so user can try again
  turnstileRef.current?.reset()
}

remove

remove(): void
Fully removes the Turnstile widget from the DOM. Use this for cleanup or when dynamically showing/hiding the widget. Example:
const handleCleanup = () => {
  turnstileRef.current?.remove()
}

getResponse

getResponse(): string | undefined
Gets the current token response from the Turnstile widget. Returns: The token string if available, or undefined if the widget hasn’t been solved yet. Example:
const handleSubmit = () => {
  const token = turnstileRef.current?.getResponse()
  if (token) {
    // Send token to your server for validation
    validateToken(token)
  } else {
    console.error('No token available')
  }
}

getResponsePromise

getResponsePromise(timeout?: number, retry?: number): Promise<string>
Gets the response of a Turnstile widget as a promise. It waits until the widget is rendered and solved.
timeout
number
default:"30000"
Timeout in milliseconds. Defaults to 30000 (30 seconds).
retry
number
default:"250"
Retry interval in milliseconds. Defaults to 250ms.
Returns: A promise that resolves with the token string. Throws: Error if the widget times out or fails to get a response. Example:
const handleSubmit = async () => {
  try {
    // Wait up to 30 seconds for the token
    const token = await turnstileRef.current?.getResponsePromise()
    
    // Validate on your server
    await fetch('/api/verify', {
      method: 'POST',
      body: JSON.stringify({ token })
    })
  } catch (error) {
    console.error('Failed to get turnstile token:', error)
  }
}
Custom timeout example:
// Wait up to 10 seconds, checking every 100ms
const token = await turnstileRef.current?.getResponsePromise(10000, 100)

isExpired

isExpired(): boolean
Checks whether the token returned by the widget is expired. Returns: true if the token is expired, false otherwise. Example:
const handleSubmit = () => {
  const expired = turnstileRef.current?.isExpired()
  
  if (expired) {
    console.log('Token expired, resetting widget')
    turnstileRef.current?.reset()
    return
  }
  
  const token = turnstileRef.current?.getResponse()
  // Proceed with token validation
}

Common Patterns

Form Submission with Token

const handleSubmit = async (e: FormEvent) => {
  e.preventDefault()
  
  try {
    const token = await turnstileRef.current?.getResponsePromise()
    
    const response = await fetch('/api/submit', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token, ...formData })
    })
    
    if (!response.ok) {
      // Reset on error
      turnstileRef.current?.reset()
    }
  } catch (error) {
    console.error('Turnstile error:', error)
  }
}

Invisible Widget Pattern

function InvisibleCaptcha() {
  const turnstileRef = useRef<TurnstileInstance>(null)
  
  const handleButtonClick = async () => {
    // Render invisible widget
    turnstileRef.current?.render()
    
    // Execute the challenge
    turnstileRef.current?.execute()
    
    // Wait for token
    const token = await turnstileRef.current?.getResponsePromise()
    
    // Use token...
  }
  
  return (
    <>
      <button onClick={handleButtonClick}>Submit</button>
      <Turnstile
        ref={turnstileRef}
        siteKey="your-site-key"
        options={{ execution: 'execute', size: 'invisible' }}
      />
    </>
  )
}

Build docs developers (and LLMs) love