LogoBetter Captcha

Imperative Handle

Imperative Handle

Overview

Each provider exposes an imperative handle so you can programmatically control the widget (e.g. reset, execute) and access provider-specific methods like reading the current response.

The handle is fully typed per provider and the implementation differs between frameworks:

  • React: Uses refs with useRef and forwardRef
  • Qwik: Uses signals with useSignal and the onReady$ event
  • SolidJS: Uses a controller pattern with createCaptchaController
  • Vue: Uses template refs or the useCaptcha composable for reactive access
  • Svelte: Uses bind:this to access component methods directly

Usage

import { useRef } from "react";
import { ReCaptcha, type ReCaptchaHandle } from "@better-captcha/react/provider/recaptcha";

export default function Example() {
  const recaptchaRef = useRef<ReCaptchaHandle>(null!);

  return (
    <>
      <ReCaptcha sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI" ref={recaptchaRef} />
      <button onClick={() => recaptchaRef.current.execute()}>Execute</button>
      <button onClick={() => recaptchaRef.current.reset()}>Reset</button>
      <button onClick={() => console.log(recaptchaRef.current.getResponse())}>Log response</button>
    </>
  );
}

The handle is available as soon as the widget is rendered. Until then, a safe no-op handle is exposed to keep type-safety.

import { component$, useSignal } from "@builder.io/qwik";
import { useCaptchaController } from "@better-captcha/qwik";
import { ReCaptcha, type ReCaptchaHandle } from "@better-captcha/qwik/provider/recaptcha";

export default component$(() => {
  const controller = useCaptchaController<ReCaptchaHandle>();

  return (
    <>
      <ReCaptcha 
        sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI" 
        controller={controller}
      />
      <button onClick$={() => controller.value?.execute()}>Execute</button>
      <button onClick$={() => controller.value?.reset()}>Reset</button>
      <button onClick$={() => console.log(controller.value?.getResponse())}>Log response</button>
    </>
  );
});

The handle is passed via the onReady$ event. Store it in a signal and use optional chaining when calling methods.

import { ReCaptcha, type ReCaptchaHandle, createCaptchaController } from "@better-captcha/solidjs/provider/recaptcha";

export default function Example() {
  const controller = createCaptchaController<ReCaptchaHandle>();

  return (
    <>
      <ReCaptcha sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI" controller={controller} />
      <button onClick={() => controller.handle()?.execute()}>Execute</button>
      <button onClick={() => controller.handle()?.reset()}>Reset</button>
      <button onClick={() => console.log(controller.handle()?.getResponse())}>Log response</button>
    </>
  );
}

The controller's handle() function returns the handle once the widget is ready, or null if not yet initialized. Always use optional chaining when calling methods.

<template>
  <ReCaptcha
    ref="recaptchaRef"
    sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
    @ready="onReady"
  />
  <button @click="execute">Execute</button>
  <button @click="reset">Reset</button>
  <button @click="logResponse">Log response</button>
</template>

<script setup lang="ts">
import { useCaptcha } from "@better-captcha/vue";
import { ReCaptcha, type ReCaptchaHandle } from "@better-captcha/vue/provider/recaptcha";

const { captchaRef, handle, execute, reset } = useCaptcha<ReCaptchaHandle>();

const recaptchaRef = captchaRef;

const onReady = (readyHandle: ReCaptchaHandle) => {
  console.log("Captcha ready", readyHandle);
};

const logResponse = () => {
  console.log(handle.value?.getResponse());
};
</script>

useCaptcha() keeps a stable handle reference and exposes helpers like execute and reset. You can also access the handle directly via ref="recaptchaRef".

<script lang="ts">
import ReCaptcha from "@better-captcha/svelte/provider/recaptcha";
import type { ReCaptchaHandle } from "@better-captcha/svelte/provider/recaptcha";

let captchaRef: ReCaptcha | undefined;

function onReady(handle: ReCaptchaHandle) {
  console.log("Captcha ready", handle);
}

function execute() {
  captchaRef?.execute();
}

function reset() {
  captchaRef?.reset();
}

function logResponse() {
  console.log(captchaRef?.getResponse());
}
</script>

<ReCaptcha
  bind:this={captchaRef}
  sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
  onready={onReady}
/>
<button onclick={execute}>Execute</button>
<button onclick={reset}>Reset</button>
<button onclick={logResponse}>Log response</button>

Use bind:this to get a reference to the component instance, then call methods directly on it. Always use optional chaining since the reference may be undefined.

Common Methods

All providers implement these methods on the handle:

Prop

Type

Provider-specific methods

Provider-specific methods (e.g. getState) are documented on each provider page: