npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@captigo/vue

v0.2.0

Published

Vue 3 components and composables for Captigo — works with any CAPTCHA adapter

Readme

@captigo/vue

Vue 3 composables and components for Captigo.

Swap providers without changing your component tree — use @captigo/turnstile, @captigo/hcaptcha, or @captigo/recaptcha.


Installation

npm install @captigo/vue @captigo/turnstile

@captigo/core is installed transitively via the adapter. Peer dependency: vue ≥ 3.3.


Quick start

Managed widget (visible checkbox)

The most common case. The widget renders automatically; you read the token reactively.

<script setup lang="ts">
import { turnstile } from "@captigo/turnstile";
import { Captcha } from "@captigo/vue";

// Create the adapter once — outside the component or via computed() to
// avoid unnecessary widget remounts on every reactive update.
const adapter = turnstile({ siteKey: "0x4AAAAAAAxxx" });

function onToken(token) {
  // Forward token.value to your server for verification.
  console.log(token.value);
}
</script>

<template>
  <form @submit.prevent="submitForm">
    <Captcha :adapter="adapter" @success="onToken" />
    <button type="submit">Submit</button>
  </form>
</template>

Invisible widget (interactive / execute on submit)

For invisible Turnstile or invisible hCaptcha, call execute() on form submit to trigger the challenge imperatively.

<script setup lang="ts">
import { ref } from "vue";
import { turnstile } from "@captigo/turnstile";
import { Captcha } from "@captigo/vue";
import type { CaptchaInstance } from "@captigo/vue";

const adapter = turnstile({
  siteKey: "0x4AAAAAAAxxx",
  execution: "execute", // invisible mode
});

const captchaRef = ref<CaptchaInstance>();

async function handleSubmit() {
  const token = await captchaRef.value!.execute("login");
  await fetch("/api/login", {
    method: "POST",
    body: JSON.stringify({ token: token.value }),
  });
}
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <Captcha ref="captchaRef" :adapter="adapter" />
    <button type="submit">Log in</button>
  </form>
</template>

useCaptcha composable

For when you need lower-level control or want to integrate with your own markup.

<script setup lang="ts">
import { turnstile } from "@captigo/turnstile";
import { useCaptcha } from "@captigo/vue";

const adapter = turnstile({ siteKey: "0x4AAAAAAAxxx" });

const { containerRef, token, execute, reset } = useCaptcha(adapter, {
  onSuccess: (t) => console.log("solved:", t.value),
  onExpire: () => console.log("token expired"),
  onError: (err) => console.error("widget error:", err),
});
</script>

<template>
  <div>
    <!-- Attach containerRef to the element that should host the widget. -->
    <div :ref="containerRef" class="my-captcha" />

    <!-- token is reactive — use it directly in templates or computed(). -->
    <p v-if="token">Token ready: {{ token.value }}</p>

    <button @click="execute('checkout')">Verify</button>
    <button @click="reset">Reset</button>
  </div>
</template>

Reactive adapter

useCaptcha accepts a plain value, a Ref, or a getter function for the adapter. Changing the adapter destroys the old widget and mounts a new one automatically.

const selectedProvider = ref<"turnstile" | "hcaptcha">("turnstile");

const adapter = computed(() =>
  selectedProvider.value === "turnstile"
    ? turnstile({ siteKey: "..." })
    : hcaptcha({ siteKey: "..." }),
);

const { containerRef, token, execute } = useCaptcha(adapter);

Server-side verification

Never trust tokens on the client. Validate them on your server before acting on them.

// server.ts (Node.js / edge)
import { verifyToken } from "@captigo/turnstile";

const result = await verifyToken(token, process.env.TURNSTILE_SECRET_KEY!);

if (!result.success) {
  throw new Error("CAPTCHA verification failed");
}

See each provider package (@captigo/turnstile, @captigo/hcaptcha, @captigo/recaptcha) for provider-specific verification functions and options.


API reference

<Captcha> component

| Prop | Type | Required | Description | |------|------|----------|-------------| | adapter | CaptchaAdapter | Yes | Provider adapter (e.g. from @captigo/turnstile) | | onSuccess | (token: CaptchaToken) => void | No | Called when a token is produced | | onError | (error: CaptchaError) => void | No | Called on an unrecoverable widget error | | onExpire | () => void | No | Called when the current token expires |

Extra attributes (id, class, style, etc.) fall through to the underlying container <div> automatically.

CaptchaInstance (accessed via template ref):

| Method | Description | |--------|-------------| | execute(action?) | Trigger the challenge. Returns Promise<CaptchaToken>. | | reset() | Reset the widget to its initial unsolved state. | | getToken() | Returns the current CaptchaToken or null. |

useCaptcha(adapter, options?)

| Parameter | Type | Description | |-----------|------|-------------| | adapter | MaybeRefOrGetter<CaptchaAdapter> | Provider adapter — can be a plain value, Ref, or getter | | options.onSuccess | (token) => void | Success callback | | options.onError | (error) => void | Error callback | | options.onExpire | () => void | Expiry callback |

Returns { containerRef, token, execute, reset }.

| Return | Type | Description | |--------|------|-------------| | containerRef | Ref<HTMLDivElement \| null> | Bind to the widget container element | | token | Ref<CaptchaToken \| null> | The current token, reactive | | execute | (action?) => Promise<CaptchaToken> | Trigger the challenge | | reset | () => void | Reset and clear token |


Notes

Adapter identity and remounts

The widget is remounted when the resolved adapter value changes. If you construct the adapter inside a computed() that re-runs frequently, the widget will remount each time. Keep adapters stable:

// Good — created once
const adapter = turnstile({ siteKey: "..." });

// Fine — computed with stable inputs
const adapter = computed(() => turnstile({ siteKey: props.siteKey }));

// Avoid — new object on every reactive update
const adapter = turnstile({ siteKey: reactiveValue.value }); // inside setup()

Passive providers (reCAPTCHA v3)

Score-based providers don't render a visible widget. The <Captcha> component still mounts an empty container; the adapter uses it to initialize. You can hide it with CSS or use useCaptcha and skip rendering containerRef entirely (call execute() to get the token on demand).

Vue Strict Mode

@captigo/vue is compatible with Vue's production build. No special handling is needed for development strict mode since Vue 3 does not remount components the way React Strict Mode does.


Documentation

@captigo/turnstile · Repository · Issues