npm package: @myrasec/eu-captcha
Description: EU CAPTCHA integration for React
Latest version: 1.0.0

This package provides an easy integration of EU CAPTCHA from Myra Security for React.

The package can be used with and without React.

For full documentation see **[docs.eu-captcha.eu](https://docs.eu-captcha.eu/)**.

You can [sign up for free](https://app.eu-captcha.eu/user-registration) to get a sitekey.

## With React

(1) Install the package.

```bash
npm i @myrasec/eu-captcha
```

(2) Import the component and render it inside your form.

```tsx
import { EuCaptcha, isEuCaptchaDone } from "@myrasec/eu-captcha";

const captchaSitekey = "EUCAPTCHA_SITE_KEY";

export function ContactForm() {
  function handleSubmit(e: React.FormEvent<HTMLFormElement>): void {
    e.preventDefault();
    if (!isEuCaptchaDone()) {
      // challenge not yet complete
      return;
    }
    // proceed with form submission
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* your fields */}
      <EuCaptcha sitekey={captchaSitekey} />
      <button type="submit">Submit</button>
    </form>
  );
}
```

You can test the integration using any fake site key.
If a site key does not exist, the captcha runs with default parameters and all traffic will be passed.

### Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `sitekey` | `string` | — | Your public sitekey (required) |
| `theme` | `string` | `"light"` | Visual theme: `"light"` or `"dark"` |
| `width` | `number` | `330` | Widget width in pixels |
| `height` | `number` | `100` | Widget height in pixels |
| `widgetId` | `string` | — | Custom widget ID. If omitted, an ID is auto-generated. Required when using `euCaptcha.execute()`. |
| `autostart` | `boolean` | `true` | Start the challenge automatically. Set to `false` to defer until `euCaptcha.execute(widgetId)` is called. |
| `onComplete` | `(token: string) => void` | — | Called with the encoded token when the challenge completes. |
| `onExpired` | `() => void` | — | Called when the token expires (60 minutes after completion). |
| `onError` | `() => void` | — | Called when the challenge fails due to a network or server error. |

### Dark theme

```tsx
<EuCaptcha sitekey={captchaSitekey} theme="dark" />
```

### Custom width

```tsx
<EuCaptcha sitekey={captchaSitekey} width={280} />
```

### Custom height

```tsx
<EuCaptcha sitekey={captchaSitekey} height={60} />
```

### Deferred execution

Set `autostart={false}` and assign a `widgetId`, then trigger the challenge manually:

```tsx
<EuCaptcha
  sitekey={captchaSitekey}
  widgetId="my-captcha"
  autostart={false}
/>

<button onClick={() => (window as any).euCaptcha.execute("my-captcha")}>Verify</button>
```

### Callbacks

```tsx
<EuCaptcha
  sitekey={captchaSitekey}
  onComplete={(token: string) => console.log("token:", token)}
  onExpired={() => console.log("token expired")}
  onError={() => console.log("challenge error")}
/>
```

### Next.js

**App Router** — components that render `<EuCaptcha>` rely on browser APIs and must be Client Components. Add `'use client'` at the top of the file:

```tsx
'use client';

import { EuCaptcha, isEuCaptchaDone } from "@myrasec/eu-captcha";
```

**Pages Router** — import the component with SSR disabled using `next/dynamic`:

```tsx
import dynamic from "next/dynamic";
import { isEuCaptchaDone } from "@myrasec/eu-captcha";

const EuCaptcha = dynamic(
  () => import("@myrasec/eu-captcha").then((m) => m.EuCaptcha),
  { ssr: false }
);
```

## Without React

To use the package without React, load the script programmatically.

(1) Install the package.

```bash
npm i @myrasec/eu-captcha
```

(2) Load the JS components asynchronously.

```ts
import { loadEuCaptcha, isEuCaptchaDone } from "@myrasec/eu-captcha";

loadEuCaptcha();
```

(3) Add the `<div>` tag to a form where EU CAPTCHA should appear.

```html
<div class="eu-captcha" data-sitekey="EUCAPTCHA_SITE_KEY"></div>
```

The following `data-*` attributes are supported on the element:

| Attribute | Default | Description |
|---|---|---|
| `data-sitekey` | — | Your public sitekey (required) |
| `data-theme` | `"light"` | Visual theme: `"light"` or `"dark"` |
| `data-width` | `330` | Widget width in pixels |
| `data-height` | `100` | Widget height in pixels |
| `data-widgetid` | auto | Custom widget ID |
| `data-autostart` | `"true"` | Set to `"false"` to defer the challenge |
| `data-callback` | — | Name of a global function called on completion |
| `data-expired-callback` | — | Name of a global function called on token expiry |
| `data-error-callback` | — | Name of a global function called on error |

## Querying state

Make sure that EU CAPTCHA is done before submitting a form to a server.

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

function handleSubmit(e: React.FormEvent<HTMLFormElement>): void {
    e.preventDefault();

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

## Listening to state change

Listen for the `euCaptchaDone` window message to be notified when the challenge completes:

```ts
function listenForCaptchaDone(msg: MessageEvent): void {
    if (msg.data.type === "euCaptchaDone") {
        // enable submit button, update state, etc.
    }
}

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