vue-cloudflare-turnstile
v1.0.3
Published
A Vue 3 component wrapper for Cloudflare Turnstile. User-friendly (re)CAPTCHA alternative
Maintainers
Readme
Vue Cloudflare Turnstile
A Vue 3 component wrapper for Cloudflare Turnstile. A user-friendly, privacy-preserving alternative to (re)CAPTCHA.
A working demo version of the widget can be found HERE
Features
- ✅ Vue 3 & TypeScript - Built with Vue 3 Composition API and full TypeScript support
- ✅ Easy Integration - Simple component-based API
- ✅ Fully Typed - Complete TypeScript definitions for all options and callbacks
- ✅ Flexible - Support for all Cloudflare Turnstile options
- ✅ Reactive - Automatic cleanup and lifecycle management
- ✅ SSR Compatible - Safe for server-side rendering
- ✅ Lightweight - Minimal bundle size with no unnecessary dependencies
Installation
npm install vue-cloudflare-turnstileyarn add vue-cloudflare-turnstilepnpm add vue-cloudflare-turnstileQuick Start
Basic Usage
<script setup lang="ts">
import { VueTurnstile } from 'vue-cloudflare-turnstile'
const handleSuccess = (token: string) => {
console.log('Turnstile token:', token)
// Send token to your server for verification
}
</script>
<template>
<VueTurnstile
site-key="YOUR_SITE_KEY"
@success="handleSuccess"
/>
</template>Global Registration
import { createApp } from 'vue'
import { VueTurnstilePlugin } from 'vue-cloudflare-turnstile'
import App from './App.vue'
const app = createApp(App)
// Register globally with default name 'VueTurnstile'
app.use(VueTurnstilePlugin)
// Or with a custom name
app.use(VueTurnstilePlugin, { componentName: 'Turnstile' })
app.mount('#app')API Reference
Props
| Prop | Type | Default | Description |
| ------------------- | ---------------------------------------------------- | ------------ | --------------------------------------------------------------------------- |
| siteKey | string | required | Your Cloudflare Turnstile site key |
| theme | 'light' \| 'dark' \| 'auto' | 'auto' | Widget theme |
| size | 'normal' \| 'compact' \| 'flexible' \| 'invisible' | 'normal' | Widget size (invisible runs challenge in background with no visible widget) |
| tabindex | number | 0 | Tab index for accessibility |
| execution | 'render' \| 'execute' | 'render' | When to execute the challenge |
| language | string | 'auto' | Language code (e.g., 'en', 'es') |
| responseField | boolean | true | Add response field to forms |
| responseFieldName | string | undefined | Custom response field name |
| retry | 'auto' \| 'never' | 'auto' | Automatic retry on failure |
| retryInterval | number | 8000 | Retry interval in milliseconds |
| refreshExpired | 'auto' \| 'manual' \| 'never' | 'auto' | Auto-refresh expired tokens |
| appearance | 'always' \| 'execute' \| 'interaction-only' | 'always' | When to show the widget |
| cData | string | undefined | Custom data to pass |
| resetKey | string \| number | undefined | Change to trigger reset |
Events
| Event | Payload | Description |
| -------------------- | -------------------- | ------------------------------------------------ |
| @success | token: string | Emitted when challenge is completed successfully |
| @error | errorCode?: string | Emitted when there is an error |
| @expired | - | Emitted when the token expires |
| @beforeInteractive | - | Emitted before the challenge is interactive |
| @afterInteractive | - | Emitted when the challenge becomes interactive |
| @unsupported | - | Emitted when the browser is unsupported |
| @timeout | - | Emitted when the challenge times out |
Methods (via Template Ref)
<script setup lang="ts">
import { ref } from 'vue'
import { VueTurnstile } from 'vue-cloudflare-turnstile'
const turnstileRef = ref<InstanceType<typeof VueTurnstile> | null>(null)
const reset = () => {
turnstileRef.value?.reset()
}
const getToken = () => {
const token = turnstileRef.value?.getResponse()
console.log('Current token:', token)
}
</script>
<template>
<VueTurnstile
ref="turnstileRef"
site-key="YOUR_SITE_KEY"
/>
</template>| Method | Returns | Description |
| --------------- | ------------------------------ | ------------------------------------------------- |
| reset() | void | Reset the widget |
| remove() | void | Remove the widget from DOM |
| getResponse() | string \| undefined | Get the current response token |
| isExpired() | boolean | Check if token has expired |
| execute() | Promise<string \| undefined> | Execute the widget (for execution mode 'execute') |
Usage Examples
With Form Validation
<script setup lang="ts">
import { ref } from 'vue'
import { VueTurnstile } from 'vue-cloudflare-turnstile'
const email = ref('')
const turnstileToken = ref('')
const isSubmitting = ref(false)
const handleTurnstileSuccess = (token: string) => {
turnstileToken.value = token
}
const submitForm = async () => {
if (!turnstileToken.value) {
alert('Please complete the Turnstile challenge')
return
}
isSubmitting.value = true
try {
const response = await fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: email.value,
turnstileToken: turnstileToken.value
})
})
if (response.ok) {
alert('Form submitted successfully!')
}
} catch (error) {
console.error('Submission error:', error)
} finally {
isSubmitting.value = false
}
}
</script>
<template>
<form @submit.prevent="submitForm">
<input
v-model="email"
type="email"
placeholder="Email"
required
/>
<VueTurnstile
site-key="YOUR_SITE_KEY"
@success="handleTurnstileSuccess"
/>
<button
type="submit"
:disabled="!turnstileToken || isSubmitting"
>
Submit
</button>
</form>
</template>Getting Your Site Key
- Go to the Cloudflare Dashboard
- Navigate to Turnstile
- Create a new site or select an existing one
- Copy your Site Key and Secret Key
- Use the Site Key in your frontend component
- Use the Secret Key for server-side verification
Test Keys (for development)
Cloudflare provides test keys for development:
- Visible (always passes):
1x00000000000000000000AA - Invisible (always passes):
2x00000000000000000000AB - Force challenge:
3x00000000000000000000FF
Server-Side Verification
IMPORTANT: Always verify the Turnstile token on your server. Never trust client-side validation alone.
TypeScript Support
This package includes full TypeScript definitions. Import types as needed:
import type {
TurnstileTheme,
TurnstileSize,
TurnstileErrorCode,
TurnstileProps,
TurnstileEmits
} from 'vue-cloudflare-turnstile'Security Best Practices
- Always validate server-side - Never rely solely on client-side validation
- Use HTTPS - Turnstile requires secure connections
- Keep secrets secure - Never expose your Secret Key in client code
- Handle errors gracefully - Implement proper error handling for network issues
- Set appropriate domains - Configure allowed domains in Cloudflare Dashboard
- Monitor usage - Track Turnstile usage in your Cloudflare Dashboard
Browser Support
Turnstile works in all modern browsers that support:
- ES6+
- WebAssembly
- JavaScript enabled
Troubleshooting
Widget not appearing
- Check that you're using a valid site key
- Ensure you're accessing the page via
http://orhttps://(notfile://) - Check browser console for errors
- Verify your domain is whitelisted in Cloudflare Dashboard
Token validation failing
- Verify you're using the correct Secret Key on the server
- Ensure tokens haven't expired (300 second lifetime)
- Check that tokens aren't being reused (each token can only be validated once)
- Review error codes in the verification response
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License - see LICENSE file for details
