Skip to content
Myra EU CAPTCHA Online Help Updated · 25 Aug 2026

HTML and JavaScript

Without a framework, you embed the widget with two lines of HTML: a script link in the head area of the page and an element in the form. Use this method also for each content management system that permits your own HTML.

Requirements

The following requirements must be met:

Requirement Value
Sitekey Public sitekey from the Details view
Access Write rights for the HTML template of the page
Server side Endpoint that verifies the token

Embed the script

Add this line to the <head> area of your page:

<script src="https://cdn.eu-captcha.eu/verify.js" async defer></script>

Note

If your website sends a Content Security Policy, then it must permit the https://cdn.eu-captcha.eu and https://api.eu-captcha.eu addresses. See Content Security Policy.

Add the widget

Add this element to each form to protect:

<div class="eu-captcha" data-sitekey="a1b2c3d4-0000-0000-0000-000000000000"></div>

Replace the value of data-sitekey with your public sitekey.

Thus, the full form is as follows:

<form method="POST" action="/your-endpoint">
  <!-- your fields -->

  <div class="eu-captcha" data-sitekey="a1b2c3d4-0000-0000-0000-000000000000"></div>

  <button type="submit">Submit</button>
</form>

During the transmission, the widget adds the hidden eu-captcha-response field with the token.

Attributes of the element

The verify.js script uses these attributes:

Attribute Default Effect
data-sitekey Public sitekey. The value is necessary.
data-theme "light" Appearance, values "light" and "dark".
data-width 330 Width of the widget in pixels.
data-height 100 Height of the widget in pixels.
data-widgetid automatic Your own identifier of the widget.
data-autostart "true" The "false" value delays the start of the challenge.
data-callback Name of a global function that is called after the challenge passed.
data-expired-callback Name of a global function that is called when the token expires.
data-error-callback Name of a global function that is called when an error occurs.

Embed the package with npm

For projects with a bundler, the same widget is available as an npm package. The package supplies TypeScript types and a version control. The widget continues to load verify.js from the CDN.

Proceed as follows to embed the package:

  • Install the package:
npm i @myrasec/eu-captcha-vanilla
  • Make a target element in the form:
<form id="contact-form">
  <!-- your fields -->
  <div id="captcha"></div>
  <button type="submit">Submit</button>
</form>
  • Render the widget into the target element:
import { renderEuCaptcha, isEuCaptchaDone } from "@myrasec/eu-captcha-vanilla";

const captchaSitekey = "EUCAPTCHA_SITE_KEY";

renderEuCaptcha("#captcha", {
  sitekey: captchaSitekey,
  onComplete: (token: string) => console.log("token:", token),
}).catch((err) => console.error("EU CAPTCHA failed to render", err));
  • The widget starts the verification in the background.

As the first argument, renderEuCaptcha accepts a CSS selector or an HTMLElement.

Options

These options are available:

Option Type Default Effect
sitekey string Public sitekey. The value is necessary.
theme string "light" Appearance, values "light" and "dark".
width number 330 Width of the widget in pixels.
height number 100 Height of the widget in pixels.
widgetId string Your own identifier of the widget. Without a value, the package makes an identifier. For euCaptcha.execute(), the value is necessary.
autostart boolean true Starts the challenge automatically. The false value delays the start until the euCaptcha.execute(widgetId) call.
onComplete (token: string) => void Is called with the token as soon as the challenge passed.
onExpired () => void Is called when the token expires. A token expires 60 minutes after the solution.
onError () => void Is called when a network error or a server error occurs.

Delay the start of the challenge

Set autostart to false and give an identifier, to start the challenge yourself:

renderEuCaptcha("#captcha", {
  sitekey: captchaSitekey,
  widgetId: "my-captcha",
  autostart: false,
});

document.getElementById("verify-btn")!.addEventListener("click", () => {
  (window as any).euCaptcha.execute("my-captcha");
});

Get the condition

Before the transmission, examine if the challenge passed:

import { isEuCaptchaDone } from "@myrasec/eu-captcha-vanilla";

function handleSubmit(e: SubmitEvent): void {
    e.preventDefault();

    if (!isEuCaptchaDone()) {
        // challenge not yet complete
        return;
    }
    // proceed with form submission
}

Warning

Without an argument, isEuCaptchaDone() is reliable only with exactly one widget without its own identifier. With more than one widget, give an identifier to each widget and get isEuCaptchaDone(widgetId).

As an alternative, wait for the euCaptchaCompleted message at the window. Examine the origin of the message, because each script and each extension in the browser can send messages:

const CAPTCHA_ORIGIN = "https://cdn.eu-captcha.eu";

function listenForCaptchaDone(msg: MessageEvent): void {
    if (msg.origin !== CAPTCHA_ORIGIN) return;
    const data = (msg.data ?? {}) as { type?: string };
    if (data.type === "euCaptchaCompleted") {
        // enable submit button, update state, etc.
    }
}

window.addEventListener("message", listenForCaptchaDone, false);

Warning

The verification in the browser controls only the operation. Always also verify the token on the server.

Remove the widget

renderEuCaptcha returns a reference. The handle.destroy() call removes the message listener, clears the target element, and resets the condition. Thus, isEuCaptchaDone() gives false again.

const handle = await renderEuCaptcha("#captcha", { sitekey: captchaSitekey });
// later
handle.destroy();

Warning

Call destroy() for each removal. Each render with a callback function adds a message listener to the window, and only destroy() removes it again. Without this call, one listener and one detached DOM subtree stay in the memory for each render.

Verify the token

Verify the token on your server. See Verify a client token.

Full example

See HTML and Django.