@anilkumarthakur/vue3-recaptcha
| Install | |
|---|---|
npm install @anilkumarthakur/vue3-recaptcha |
|
| Latest Version: | 1.0.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 26, 2026 |
| Links: | GitHub · npm |
@anilkumarthakur/vue3-recaptcha
A fully-typed Vue 3 plugin for Google reCAPTCHA — supports v2 checkbox, v2 invisible, and v3 score-based out of the box.
Features
- Three reCAPTCHA modes — v2 checkbox, v2 invisible, v3 score-based
- Component + composable APIs — use whichever fits your pattern
v-modelsupport — bind tokens reactively with no boilerplate- TypeScript-first — full type coverage, component instance types included
- Script caching — loads the Google script once, shared across all instances
- Token expiry management — auto-clears v3 tokens before Google's 2-minute expiry
- SSR-safe — DOM checks guard all browser APIs
- Lightweight — Vue is a peer dep, zero other runtime dependencies
Contents
- Installation
- Quick Start
- Plugin Options
- Components
- Composables
- TypeScript
- Running Locally
- Building
- Contributing
Installation
npm install @anilkumarthakur/vue3-recaptcha
# or
pnpm add @anilkumarthakur/vue3-recaptcha
# or
yarn add @anilkumarthakur/vue3-recaptcha
Requirements: Vue >= 3.4, Node >= 18
Quick Start
1. Register the plugin
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { VueRecaptchaPlugin } from '@anilkumarthakur/vue3-recaptcha'
const app = createApp(App)
app.use(VueRecaptchaPlugin, {
siteKey: import.meta.env.VITE_RECAPTCHA_SITE_KEY_V3,
version: 'v3' // 'v2' | 'v3'
})
app.mount('#app')
2. Use a component
<template>
<form @submit.prevent="submit">
<RecaptchaV3 v-model="token" @verify="onVerify" />
<button type="submit">Submit</button>
</form>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const token = ref('')
function onVerify(t: string) {
console.log('Token ready:', t)
}
async function submit() {
// send token to your backend for server-side verification
}
</script>
Components (
RecaptchaV3,RecaptchaV2Checkbox,RecaptchaV2Invisible) are auto-registered globally when you install the plugin. No per-component import needed.
Plugin Options
| Option | Type | Default | Description |
|---|---|---|---|
siteKey |
string |
— | Required. Your Google reCAPTCHA site key |
version |
'v2' | 'v3' |
'v3' |
Which reCAPTCHA version to load |
autoLoad |
boolean |
true |
Load the Google script immediately on install |
language |
string |
— | Force a language (e.g. 'fr', 'de') |
scriptUrl |
string |
— | Override the Google script URL entirely |
action |
string |
'submit' |
Default action name for v3 analytics |
onLoad |
() => void |
— | Callback when script loads successfully |
onError |
(e: Error) => void |
— | Callback when script fails to load |
Components
RecaptchaV3
Invisible, score-based reCAPTCHA. Does not render any UI — Google shows a badge in the corner.
<template>
<RecaptchaV3
v-model="token"
action="login"
badge="bottomright"
@verify="onVerify"
@error="onError"
ref="recaptcha"
/>
<button @click="recaptcha?.execute('checkout')">Pay Now</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { RecaptchaV3Instance } from '@anilkumarthakur/vue3-recaptcha'
const token = ref('')
const recaptcha = ref<RecaptchaV3Instance>()
function onVerify(t: string) {
/* verified */
}
function onError(e: Error) {
/* handle error */
}
</script>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
siteKey |
string |
plugin config | Override the site key per-instance |
action |
string |
'submit' |
Action label for Google Analytics |
badge |
'bottomright' | 'bottomleft' | 'inline' |
'bottomright' |
Badge position |
hideBadge |
boolean |
false |
Hide the reCAPTCHA badge |
modelValue |
string |
— | v-model token binding |
Emits: update:modelValue, verify(token), error(Error), load
Ref Methods
| Method | Returns | Description |
|---|---|---|
execute(action?) |
Promise<string> |
Generate a new token |
load() |
Promise<void> |
Manually trigger script load |
Ref State: isLoaded, isLoading, error
RecaptchaV2Checkbox
The classic "I'm not a robot" checkbox widget.
<template>
<RecaptchaV2Checkbox
v-model="token"
theme="light"
size="normal"
@verify="onVerify"
@expire="onExpire"
ref="checkbox"
/>
<button @click="checkbox?.reset()">Reset</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { RecaptchaV2CheckboxInstance } from '@anilkumarthakur/vue3-recaptcha'
const token = ref('')
const checkbox = ref<RecaptchaV2CheckboxInstance>()
function onVerify(t: string) {
/* user checked */
}
function onExpire() {
token.value = ''
}
</script>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
siteKey |
string |
plugin config | Override the site key per-instance |
theme |
'light' | 'dark' |
'light' |
Widget color theme |
size |
'normal' | 'compact' |
'normal' |
Widget size |
tabindex |
number |
0 |
Accessibility tabindex |
modelValue |
string |
— | v-model token binding |
Emits: update:modelValue, verify(token), expire, error(Error), load
Ref Methods: reset(), getResponse(): string
Ref State: widgetId
RecaptchaV2Invisible
Invisible v2 — no UI, fires on demand, returns a token via promise.
<template>
<RecaptchaV2Invisible v-model="token" badge="bottomright" @verify="onVerify" ref="invisible" />
<button @click="verify">Submit</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { RecaptchaV2InvisibleInstance } from '@anilkumarthakur/vue3-recaptcha'
const token = ref('')
const invisible = ref<RecaptchaV2InvisibleInstance>()
async function verify() {
const t = await invisible.value?.execute()
// proceed with form submission
}
function onVerify(t: string) {
/* token ready */
}
</script>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
siteKey |
string |
plugin config | Override the site key per-instance |
badge |
'bottomright' | 'bottomleft' | 'inline' |
'bottomright' |
Badge position |
tabindex |
number |
0 |
Accessibility tabindex |
modelValue |
string |
— | v-model token binding |
Emits: update:modelValue, verify(token), expire, error(Error), load
Ref Methods: execute(): Promise<string>, reset(), getResponse(): string
Composables
Composables give you full programmatic control without mounting a component. Useful when you want to decouple token generation from your template entirely.
useRecaptchaV3
import { useRecaptchaV3 } from '@anilkumarthakur/vue3-recaptcha'
const { execute, token, isReady, isLoading, error } = useRecaptchaV3({
action: 'checkout'
})
async function handleSubmit() {
try {
const t = await execute() // generates a fresh token
await api.post('/checkout', { recaptchaToken: t })
} catch (e) {
console.error('reCAPTCHA failed', e)
}
}
| Option | Type | Description |
|---|---|---|
siteKey |
string |
Override plugin-level site key |
action |
string |
Default action for execute() calls |
| Return | Type | Description |
|---|---|---|
token |
Readonly<Ref<string>> |
Current token (clears after 110s) |
isReady |
Readonly<Ref<boolean>> |
Script has loaded |
isLoading |
Readonly<Ref<boolean>> |
Token generation in progress |
error |
Readonly<Ref<Error | null>> |
Last error |
execute(action?) |
Promise<string> |
Generate a token |
useRecaptchaV2
import { ref, onMounted } from 'vue'
import { useRecaptchaV2 } from '@anilkumarthakur/vue3-recaptcha'
const container = ref<HTMLElement>()
const { render, execute, reset, token, error } = useRecaptchaV2({
theme: 'dark',
size: 'normal'
})
onMounted(async () => {
await render(container.value!)
})
async function handleSubmit() {
const t = await execute()
// send token to backend
}
| Return | Type | Description |
|---|---|---|
token |
Readonly<Ref<string>> |
Current token |
widgetId |
Readonly<Ref<number | null>> |
Google widget ID |
isReady |
Readonly<Ref<boolean>> |
Widget rendered |
isLoading |
Readonly<Ref<boolean>> |
Execution in progress |
error |
Readonly<Ref<Error | null>> |
Last error |
render(el) |
Promise<number> |
Mount widget into a DOM element |
execute() |
Promise<string> |
Trigger invisible verification |
reset() |
void |
Reset widget state |
getResponse() |
string |
Get current token synchronously |
TypeScript
All component instance types are exported for use with template refs:
import type {
RecaptchaV3Instance,
RecaptchaV2CheckboxInstance,
RecaptchaV2InvisibleInstance
} from '@anilkumarthakur/vue3-recaptcha'
Other exported types:
import type {
RecaptchaVersion, // 'v2' | 'v3'
RecaptchaTheme, // 'light' | 'dark'
RecaptchaSize, // 'normal' | 'compact' | 'invisible'
RecaptchaBadgePosition, // 'bottomright' | 'bottomleft' | 'inline'
VueRecaptchaPluginOptions
} from '@anilkumarthakur/vue3-recaptcha'
Running Locally
# 1. Clone
git clone https://github.com/anilkumarthakur60/vue3-recaptcha.git
cd vue3-recaptcha
# 2. Install dependencies
npm install
# 3. Set up your reCAPTCHA key
# Create a .env file in the project root:
echo "VITE_RECAPTCHA_SITE_KEY_V3=your_key_here" > .env
# 4. Start the demo app
npm run dev
# Opens at http://localhost:3000
Get a free site key at google.com/recaptcha/admin. Add
localhostas an allowed domain when creating the key.
Building
Build the npm package
npm run build
Outputs to dist/ in multiple formats:
| File | Format | Use case |
|---|---|---|
dist/index.es.js |
ES Module | Vite / modern bundlers |
dist/index.cjs |
CommonJS | Node / older bundlers |
dist/index.iife.js |
IIFE | CDN / <script> tags |
dist/index.d.ts |
TypeScript | Type declarations |
Build the demo app
npm run build:prod
Builds the interactive demo site. Preview the output locally:
npm run preview
Other scripts
npm run type-check # TypeScript validation only (no emit)
npm run test # Run Vitest test suite
npm run lint # ESLint with auto-fix
npm run format # Prettier formatting
Contributing
Contributions are welcome — whether it's a bug fix, a new feature, or improved docs.
Getting Started
-
Fork the repo and create your branch from
maingit checkout -b feat/your-feature -
Make changes — keep them focused and well-scoped
-
Add or update tests in
tests/(Vitest + Vue Test Utils) -
Run the checks before pushing
npm run type-check npm run lint npm run test -
Open a PR against
mainwith a clear description of what changed and why
Project Layout
src/
├── package/ # Library source (published to npm)
│ ├── index.ts # Plugin entry + all exports
│ ├── components/ # RecaptchaV3, RecaptchaV2Checkbox, RecaptchaV2Invisible
│ ├── composables/ # useRecaptchaV3, useRecaptchaV2
│ ├── types/ # TypeScript interfaces and types
│ └── utils/ # Script loader and helpers
└── views/ # Demo app (HomeView etc.) — not published
tests/ # Vitest specs mirroring src/package/
vite.config.ts # Dev server config (demo app)
vite.config.prod.ts # Library build config (npm package)
What to Work On
- Issues labeled
good first issueorhelp wanted - Nuxt module wrapper
- reCAPTCHA Enterprise support
- Additional composable test coverage
- Docs improvements
License
Related Packages
A lightweight Vue 3 component library supporting Google reCAPTCHA v2 and v3 inte...
Lightweight Vue 3 reCAPTCHA v2 (checkbox) and v3 (score-based) component with Ty...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 1.0.0 | ^3.4.0 | MIT | |
| 0.0.10 | ^3.4.0 | MIT | |
| 0.0.9 | ^3.4.0 | MIT | |
| 0.0.8 | ^3.4.29 | ||
| 0.0.7 | ^3.4.29 | ||
| 0.0.6 | ^3.4.29 | ||
| 0.0.5 | ^3.4.29 | ||
| 0.0.4 | ^3.4.29 | ||
| 0.0.3 | ^3.4.29 | ||
| 0.0.2 | ^3.4.29 | ||
| 0.0.1 | ^3.4.29 |