Multiple reCAPTCHA Widgets on One Page: Explicit Rendering, Widget IDs, and Reset Done Right

Multiple reCAPTCHA Widgets on One Page: Explicit Rendering, Widget IDs, and Reset Done Right

A login modal in the header, a newsletter form in the footer, a contact form in between — and only one of the three CAPTCHAs actually works. Multi-widget pages are where the copy-paste reCAPTCHA snippet quietly gives up. The cure is the explicit rendering API: widget IDs, per-form responses, targeted resets, and a couple of traps worth knowing before your modal eats an afternoon.

rCAPTCHA Team
rCAPTCHA Team
June 10, 2026 · 9 min read

The standard integration — drop in api.js, add a <div class="g-recaptcha"> — was designed for the common case of one form per page. Push past it and the cracks appear fast, as a decade of threads attests, from the official reCAPTCHA group to Drupal's long-running multi-instance issue: second widgets that never render, both forms sharing one g-recaptcha-response field so the wrong token gets posted, and modals injected after page load showing an empty box where a checkbox should be. None of these are mysteries — they are all the automatic-rendering convenience layer hitting its design limit.

The Fix Is Official: Render Explicitly

Try rCAPTCHA

Experience the technology discussed in this article.

Learn More →

Google's display documentation provides a first-class API for exactly this situation. Load the script in explicit mode with an onload callback, then render each widget yourself and keep the IDs:

<script src="https://www.google.com/recaptcha/api.js
         ?onload=onCaptchaReady&render=explicit"
        async defer></script>

<script>
var loginWidget, contactWidget;
function onCaptchaReady() {
  loginWidget = grecaptcha.render("login-captcha", {
    sitekey: SITE_KEY,
  });
  contactWidget = grecaptcha.render("contact-captcha", {
    sitekey: SITE_KEY,
  });
}
</script>

Every grecaptcha.render() call returns a widget ID — the handle the rest of the API keys on. With IDs in hand, each form reads its own response and resets only itself:

// On login submit:
var token = grecaptcha.getResponse(loginWidget);

// After the server consumed (or rejected) it:
grecaptcha.reset(loginWidget);   // contact form untouched

Omit the ID and both calls default to the first widget on the page — which is precisely why "the footer form always sends an empty token" is the classic symptom. If your backend is logging missing-input-response or rejecting tokens that look fine, widget-ID confusion belongs on the suspect list right next to the server-side issues in our token-debugging guide.

The Traps Between You and a Clean Multi-Widget Page

  • Rendering twice into one container. render() throws if the element already hosts a widget ("reCAPTCHA has already been rendered in this element"). Track what you have rendered — especially in SPAs and tab UIs where initialization code can run again.
  • Forms injected after load. A modal fetched via AJAX arrives after automatic rendering has come and gone, so its g-recaptcha div stays inert forever. Explicit mode solves it cleanly: call render() right after inserting the markup.
  • One token, two forms. Tokens are single-use; "borrowing" the other form's response — accidentally, via the shared hidden-field name — earns a timeout-or-duplicate from siteverify, the error we autopsy in the token-expiry guide. Always read the response by widget ID.
  • Invisible widgets multiply the confusion. The invisible variant binds to buttons and fires callbacks; with several on one page, render each explicitly and call grecaptcha.execute(widgetId) per button. Mixing key types while refactoring earns the "Invalid key type" banner — see our key-type untangling guide.
  • v3 needs none of this. Score-based reCAPTCHA has no widgets to multiply: call execute() per protected action with a distinct action name (login, contact, newsletter) and verify the name server-side. If your page hosts five forms, that is five actions, not five widgets.

Step Back: Per-Form Friction Is a Choice

It is worth asking why the page needs three challenges in the first place. Each visible widget is friction billed to every legitimate visitor, and the engineering above exists to manage that friction's bookkeeping. Modern verification design pushes the other way — assess the visitor once, at the session level, and let every form on the page benefit. That is the model behavioral systems such as rCAPTCHA use, and even within Google's ecosystem, v3's action-per-flow design acknowledges the same point. Multi-widget juggling is sometimes genuinely necessary — but if you are rendering your fourth checkbox onto one page, the architecture, not the API, is the thing to question.

People Also Ask: Multi-Widget FAQ

Can I have two reCAPTCHA widgets on the same page?

Yes. Load the script with render=explicit and an onload callback, call grecaptcha.render() once per container, and store each returned widget ID for getResponse and reset calls.

Why does only my first reCAPTCHA work?

API calls without a widget ID target the first widget rendered on the page, and forms commonly share the default g-recaptcha-response field. Use explicit rendering and always pass the widget ID.

How do I add reCAPTCHA to a form loaded via AJAX?

Automatic rendering only processes elements present when the script initializes. Render the widget manually with grecaptcha.render() immediately after inserting the form's markup into the DOM.

Do multiple widgets need different site keys?

No — one site key can back every widget on the page (and the site). The widget ID, not the key, distinguishes instances. Different key types (v2 checkbox vs invisible vs v3) are a different matter and cannot be mixed under one key.

Conclusion

Multiple CAPTCHAs on one page fail for an unglamorous reason: the convenience layer renders what it finds at load time and answers for one widget unless told otherwise. Explicit rendering replaces guesswork with handles — one render per container, one widget ID per form, responses and resets addressed precisely. Adopt it for any page with more than one form, render dynamic content at injection time, and spend five minutes asking whether the page really needs that many challenges at all. The bookkeeping is easy once it is explicit — it was only ever the implicit magic that hurt.

Sources & Further Reading

rCAPTCHA Blog
rCAPTCHA Blog

Insights on web security and bot detection

More from this blog →

Responses

No responses yet. Be the first to share your thoughts!