Skip to main content

What is Invisible reCAPTCHA?

Invisible reCAPTCHA validates users transparently without requiring them to click a checkbox. It only shows a challenge when suspicious activity is detected, providing a seamless user experience.
See Google’s invisible reCAPTCHA documentation for more details on configuration and best practices.

Basic Configuration

To use invisible reCAPTCHA, set the size prop to "invisible":
import ReCAPTCHA from "react-google-recaptcha";

<ReCAPTCHA
  size="invisible"
  sitekey="Your client site key"
  onChange={onChange}
/>
With invisible reCAPTCHA, you must manually call the execute() method to trigger verification. The captcha won’t run automatically.

Using execute() Method

The execute() method programmatically invokes the reCAPTCHA challenge. You typically call this on form submission.
import React from "react";
import ReCAPTCHA from "react-google-recaptcha";
import ReactDOM from "react-dom";

const recaptchaRef = React.createRef();

function onChange(token) {
  console.log("Captcha token:", token);
  // Send token to your backend for verification
}

ReactDOM.render(
  <form onSubmit={() => { recaptchaRef.current.execute(); }}>
    <input type="text" name="email" placeholder="Email" />
    <input type="password" name="password" placeholder="Password" />
    <ReCAPTCHA
      ref={recaptchaRef}
      size="invisible"
      sitekey="Your client site key"
      onChange={onChange}
    />
    <button type="submit">Submit</button>
  </form>,
  document.body
);

How It Works

  1. User submits the form
  2. execute() is called, triggering reCAPTCHA verification
  3. If verification passes, onChange callback is invoked with the token
  4. Your application can then proceed with form submission

Using executeAsync() with Promises

For a more modern, promise-based approach, use executeAsync(). This method returns a promise that resolves to the token.
import React from "react";
import ReCAPTCHA from "react-google-recaptcha";
import ReactDOM from "react-dom";

const ReCAPTCHAForm = (props) => {
  const recaptchaRef = React.useRef();

  const onSubmitWithReCAPTCHA = async () => {
    const token = await recaptchaRef.current.executeAsync();

    // apply to form data
    console.log("Token:", token);
  }

  return (
    <form onSubmit={onSubmitWithReCAPTCHA}>
      <input type="text" placeholder="Username" />
      <ReCAPTCHA
        ref={recaptchaRef}
        size="invisible"
        sitekey="Your client site key"
      />
      <button type="submit">Submit</button>
    </form>
  )
}

ReactDOM.render(
  <ReCAPTCHAForm />,
  document.body
);
With executeAsync(), you don’t need to provide an onChange callback since the token is returned via the promise.

Complete Example with Error Handling

import React from "react";
import ReCAPTCHA from "react-google-recaptcha";

function LoginForm() {
  const recaptchaRef = React.useRef();
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);

    try {
      // Get the reCAPTCHA token
      const token = await recaptchaRef.current.executeAsync();
      
      // Submit to your API
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          email: e.target.email.value,
          password: e.target.password.value,
          recaptchaToken: token
        })
      });

      if (response.ok) {
        // Handle successful login
        console.log("Login successful");
      } else {
        setError("Login failed");
      }
    } catch (err) {
      setError("An error occurred: " + err.message);
    } finally {
      setLoading(false);
      // Reset reCAPTCHA for next submission
      recaptchaRef.current.reset();
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" name="email" required />
      <input type="password" name="password" required />
      
      <ReCAPTCHA
        ref={recaptchaRef}
        size="invisible"
        sitekey="Your client site key"
      />
      
      {error && <div className="error">{error}</div>}
      <button type="submit" disabled={loading}>
        {loading ? "Submitting..." : "Login"}
      </button>
    </form>
  );
}

Badge Positioning

The invisible reCAPTCHA displays a small badge on your page. You can control its position with the badge prop:
<ReCAPTCHA
  size="invisible"
  sitekey="Your client site key"
  badge="bottomright"
/>

Available Positions

<ReCAPTCHA
  size="invisible"
  sitekey="Your client site key"
  badge="bottomright"
/>
Default position - fixed to the bottom right corner of the page.
The badge prop only works with invisible reCAPTCHA (size="invisible"). It has no effect on normal or compact sizes.

Hiding the Badge

You can hide the reCAPTCHA badge, but there are important legal requirements. See the Hiding the Badge guide for details.

Comparison: execute() vs executeAsync()

Use when:
  • You prefer callback-based patterns
  • You need to handle the token in a separate function
  • You’re using class components
function onChange(token) {
  // Handle token here
}

<ReCAPTCHA
  ref={recaptchaRef}
  size="invisible"
  sitekey="key"
  onChange={onChange}
/>

// Later, in your submit handler:
recaptchaRef.current.execute();
Use when:
  • You prefer async/await syntax
  • You want to handle the token inline
  • You’re using functional components
const handleSubmit = async () => {
  const token = await recaptchaRef.current.executeAsync();
  // Use token immediately
}

<ReCAPTCHA
  ref={recaptchaRef}
  size="invisible"
  sitekey="key"
/>

Best Practices

Reset after submission: Always call reset() after processing the form to allow users to resubmit if needed.
One-time use tokens: Each reCAPTCHA token can only be verified once on your backend. Generate a new token for each submission.
Token expiration: Tokens expire after a few minutes. If a user takes too long to submit, you may need to generate a new token.

Build docs developers (and LLMs) love