Google reCAPTCHA v3 with Angular

Recently I had to implement a recaptcha in one of our projects. I was looking for an npm package, but even the popular ng-recaptcha package didn't support Angular version 18.
After checking the javascript code I came up with a simple solution, by building my own typescript service to wrap the javascript library from google.

Creating the service

The following code is pretty simple:

recaptcha.service.ts import { DOCUMENT } from "@angular/common"; import { Inject, Injectable } from "@angular/core"; declare var grecaptcha: any; @Injectable({ providedIn: 'root' }) export class RecaptchaService { private siteKey: string | null = null; private loaded: boolean = false; constructor(@Inject(DOCUMENT) private readonly document: Document) { } public load(siteKey: string): void { if(this.loaded) { return; } this.siteKey = siteKey; const script = document.createElement('script'); script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`; this.document.head.appendChild(script); this.loaded = true; } public execute(action: string, callback: (token: string) => void): void { if(!this.siteKey) { throw new Error('Recaptcha site key is not set.'); } grecaptcha.ready(() => { grecaptcha.execute(this.siteKey!, { action }) .then(callback); }); } }

With declare var grecaptcha: any; we basically make the javascript object accessible in our typescript code.

In the register method we dynamically append a script tag to the header of our document. This is necessary because angular will sanitize script-tags directly placed in your component html files. Also the advantage is that we only load the script file when it's actually needed.

Using the service

To use the services you only need to call both methods from the service. Here is a pretty simple example:

my.component.ts import { RecaptchaService } from 'src/app/recaptcha.service'; export class MyComponent implements OnInit { private readonly formGroup = new FormGroup(); constructor(private readonly recaptchaService: RecaptchaService, private readonly myApiService: MyApiService) { } ngOnInit(): void { this.recaptchaService.load('<Your Site Key>'); } protected onSubmit(): void { this.formGroup.markAllAsTouched(); if (this.formGroup.valid) { this.recaptchaService.execute('myAction', recaptchaToken => { const model = this.formGroup.getRawValue(); this.myApiService.myAction(recaptchaToken, model) .subscribe(result => { ... }); }); } } }

And that's basically it. Don't forget to verify the recaptcha token in your API request against response from google (https://developers.google.com/recaptcha/docs/verify).