@anilkumarthakur/vue3-recaptcha

A Vue 3 plugin for Google reCAPTCHA v2 (checkbox & invisible) and v3 integration
1,576
Install
npm install @anilkumarthakur/vue3-recaptcha
Latest Version:1.0.0
Vue:3
License:MIT
Last Updated:Oct 26, 2026
Links: GitHub  ·  npm
Maintainer: anilkumarthakur

@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.

npm version npm downloads License: MIT


Features

  • Three reCAPTCHA modes — v2 checkbox, v2 invisible, v3 score-based
  • Component + composable APIs — use whichever fits your pattern
  • v-model support — 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

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 localhost as 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

  1. Fork the repo and create your branch from main

    git checkout -b feat/your-feature
    
  2. Make changes — keep them focused and well-scoped

  3. Add or update tests in tests/ (Vitest + Vue Test Utils)

  4. Run the checks before pushing

    npm run type-check
    npm run lint
    npm run test
    
  5. Open a PR against main with 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 issue or help wanted
  • Nuxt module wrapper
  • reCAPTCHA Enterprise support
  • Additional composable test coverage
  • Docs improvements

License

MIT © Anil Kumar Thakur

Related Packages

google-recaptcha-vue3

A lightweight Vue 3 component library supporting Google reCAPTCHA v2 and v3 inte...

3,291 1
recaptcha-vue

Lightweight Vue 3 reCAPTCHA v2 (checkbox) and v3 (score-based) component with Ty...

1,537 7

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