npm package: @myrasec/eu-captcha-angular21
Description: EU CAPTCHA integration for **Angular 21**.
Latest version: 1.0.1

# @myrasec/eu-captcha-angular21

EU CAPTCHA integration for **Angular 21**.

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.

> **Other Angular versions**
> | Package | Angular |
> |---------|---------|
> | [`@myrasec/eu-captcha-angular19`](https://www.npmjs.com/package/@myrasec/eu-captcha-angular19) | Angular 19 |
> | [`@myrasec/eu-captcha-angular20`](https://www.npmjs.com/package/@myrasec/eu-captcha-angular20) | Angular 20 |
> | [`@myrasec/eu-captcha-angular21`](https://www.npmjs.com/package/@myrasec/eu-captcha-angular21) | Angular 21 ← you are here |

## Installation

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

## Basic usage

### Standalone component

```ts
import { Component } from '@angular/core';
import { EuCaptchaComponent, isEuCaptchaDone } from '@myrasec/eu-captcha-angular21';

@Component({
  standalone: true,
  imports: [EuCaptchaComponent],
  template: `
    <form (submit)="handleSubmit($event)">
      <!-- your fields -->
      <eu-captcha sitekey="EUCAPTCHA_SITE_KEY" />
      <button type="submit">Submit</button>
    </form>
  `,
})
export class MyFormComponent {
  handleSubmit(event: Event): void {
    event.preventDefault();
    if (!isEuCaptchaDone()) {
      // challenge not yet complete
      return;
    }
    // proceed with form submission
  }
}
```

### NgModule-based app

```ts
import { NgModule } from '@angular/core';
import { EuCaptchaModule } from '@myrasec/eu-captcha-angular21';

@NgModule({
  imports: [EuCaptchaModule],
})
export class AppModule {}
```

```html
<!-- in your template -->
<eu-captcha sitekey="EUCAPTCHA_SITE_KEY" />
```

You can test the integration using any fake sitekey. If a sitekey does not exist, the captcha runs with default parameters which allow traffic to pass.

## Inputs

| Input | 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. Needed when calling `euCaptcha.execute()`. |
| `autostart` | `boolean` | `true` | Start the challenge automatically on mount. Set to `false` to defer until `euCaptcha.execute(widgetId)` is called. |

## Outputs

| Output | Payload | Description |
|---|---|---|
| `completed` | `string` | Emitted with the encoded token when the challenge completes. |
| `expired` | — | Emitted when the token expires (60 minutes after completion). |
| `error` | — | Emitted when the challenge fails due to a network or server error. |

## Dark theme

```html
<eu-captcha sitekey="EUCAPTCHA_SITE_KEY" theme="dark" />
```

## Custom width

```html
<eu-captcha sitekey="EUCAPTCHA_SITE_KEY" [width]="280" />
```

## Custom height

```html
<eu-captcha sitekey="EUCAPTCHA_SITE_KEY" [height]="60" />
```

## Deferred execution

Set `[autostart]="false"` and provide a `widgetId`, then trigger the challenge manually:

```html
<eu-captcha
  sitekey="EUCAPTCHA_SITE_KEY"
  widgetId="my-captcha"
  [autostart]="false"
/>

<button (click)="euCaptcha.execute('my-captcha')">Verify</button>
```

## Listening to events

```html
<eu-captcha
  sitekey="EUCAPTCHA_SITE_KEY"
  (completed)="onToken($event)"
  (expired)="onExpired()"
  (error)="onError()"
/>
```

```ts
onToken(token: string): void {
  console.log('token:', token);
}

onExpired(): void {
  console.log('token expired');
}

onError(): void {
  console.log('challenge error');
}
```

## Querying state

Before submitting a form to a server, ensure EU CAPTCHA is done:

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

function handleSubmit(): void {
    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
import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({ /* ... */ })
export class MyComponent implements OnInit, OnDestroy {
  private listener = (msg: MessageEvent) => {
    if (msg.data.type === 'euCaptchaDone') {
      // enable submit button, update state, etc.
    }
  };

  ngOnInit(): void {
    window.addEventListener('message', this.listener, false);
  }

  ngOnDestroy(): void {
    window.removeEventListener('message', this.listener, false);
  }
}
```