JSPM

@saivarunvaidya/recaptcha-universal-sdk

1.0.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 3
  • Score
    100M100P100Q70808F
  • License MIT

Framework-agnostic reCAPTCHA v2 SDK with adapters for React, Angular, Vue, and vanilla JS

Package Exports

  • @saivarunvaidya/recaptcha-universal-sdk
  • @saivarunvaidya/recaptcha-universal-sdk/angular
  • @saivarunvaidya/recaptcha-universal-sdk/react
  • @saivarunvaidya/recaptcha-universal-sdk/vanilla
  • @saivarunvaidya/recaptcha-universal-sdk/vue

Readme

@recaptcha/universal-sdk

Framework-agnostic reCAPTCHA v2 SDK with adapters for React, Angular, Vue, and vanilla JavaScript

npm version License: MIT

๐ŸŽฏ Features

  • โœ… Framework-Agnostic Core - Pure TypeScript, zero dependencies
  • โœ… Multiple Framework Adapters - React, Vue, Angular, Vanilla JS
  • โœ… TypeScript First - Full type definitions included
  • โœ… SSR Safe - Works with server-side rendering
  • โœ… Tree-Shakeable - Only import what you need
  • โœ… Production Ready - Comprehensive testing and error handling
  • โœ… Backend Integration - Built-in verification service support
  • โœ… reCAPTCHA v2 - Support for Checkbox and Invisible variants

๐Ÿ“ฆ Installation

npm install @recaptcha/universal-sdk

Or with yarn:

yarn add @recaptcha/universal-sdk

๐Ÿš€ Quick Start

React

import { useRecaptcha } from '@recaptcha/universal-sdk/react';

function MyForm() {
  const { containerRef, token, isReady, verify } = useRecaptcha({
    siteKey: 'your-site-key',
    clientId: 'react-app',
    verifyUrl: 'https://api.example.com/captcha/verify',
    options: {
      size: 'normal',
      theme: 'light',
      callback: (token) => console.log('Token:', token)
    }
  });

  const handleSubmit = async () => {
    if (token) {
      const result = await verify();
      console.log('Verified:', result.success);
    }
  };

  return (
    <div>
      <div ref={containerRef}></div>
      <button onClick={handleSubmit} disabled={!token}>
        Submit
      </button>
    </div>
  );
}

Vue 3

<script setup>
import { useRecaptcha } from '@recaptcha/universal-sdk/vue';

const { containerRef, token, isReady, verify } = useRecaptcha({
  siteKey: 'your-site-key',
  clientId: 'vue-app',
  verifyUrl: 'https://api.example.com/captcha/verify',
  options: {
    size: 'normal'
  }
});

const handleSubmit = async () => {
  if (token.value) {
    const result = await verify();
    console.log('Verified:', result.success);
  }
};
</script>

<template>
  <div>
    <div ref="containerRef"></div>
    <button @click="handleSubmit" :disabled="!token">Submit</button>
  </div>
</template>

Angular

import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
import { RecaptchaService } from '@recaptcha/universal-sdk/angular';

@Component({
  selector: 'app-form',
  template: `
    <div #recaptchaContainer></div>
    <button (click)="handleSubmit()" [disabled]="!(token$ | async)">
      Submit
    </button>
  `
})
export class FormComponent implements OnInit, AfterViewInit {
  @ViewChild('recaptchaContainer', { static: false }) container!: ElementRef;
  
  token$ = this.recaptchaService.token$;
  
  constructor(private recaptchaService: RecaptchaService) {}
  
  async ngOnInit() {
    await this.recaptchaService.init({
      siteKey: 'your-site-key',
      clientId: 'angular-app',
      verifyUrl: 'https://api.example.com/captcha/verify'
    });
  }
  
  ngAfterViewInit() {
    this.recaptchaService.render(this.container.nativeElement);
  }
  
  async handleSubmit() {
    const result = await this.recaptchaService.verify();
    console.log('Verified:', result.success);
  }
}

Vanilla JavaScript

import { VanillaRecaptcha } from '@recaptcha/universal-sdk/vanilla';

const recaptcha = new VanillaRecaptcha({
  siteKey: 'your-site-key',
  clientId: 'vanilla-app',
  verifyUrl: 'https://api.example.com/captcha/verify',
  options: {
    size: 'normal',
    callback: (token) => {
      console.log('Token received:', token);
      handleToken(token);
    }
  }
});

// Initialize
await recaptcha.init();

// Render
const container = document.getElementById('recaptcha-container');
recaptcha.render(container);

// Verify
async function handleToken(token) {
  const result = await recaptcha.verify(token);
  console.log('Verified:', result.success);
}

๐Ÿ“š API Documentation

Core Configuration

interface RecaptchaConfig {
  siteKey: string;              // Your reCAPTCHA site key
  clientId?: string;            // Client ID for backend verification
  verifyUrl?: string;           // Backend verification endpoint
  language?: string;            // Language code (e.g., 'en', 'es')
  options?: {
    size?: 'normal' | 'compact' | 'invisible';
    theme?: 'light' | 'dark';
    badge?: 'bottomright' | 'bottomleft' | 'inline';
    tabindex?: number;
    callback?: (token: string) => void;
    'expired-callback'?: () => void;
    'error-callback'?: () => void;
  };
}

React Hook API

const {
  containerRef,     // Ref for the container element
  execute,          // Execute invisible reCAPTCHA
  reset,            // Reset the widget
  getResponse,      // Get current token
  verify,           // Verify token with backend
  token,            // Current token (reactive)
  isReady,          // Is reCAPTCHA loaded
  isLoading,        // Is loading
  error,            // Error message
  widgetId          // Widget ID
} = useRecaptcha(config);

Vue Composable API

const {
  containerRef,     // Template ref for container
  execute,          // Execute invisible reCAPTCHA
  reset,            // Reset the widget
  getResponse,      // Get current token
  verify,           // Verify token with backend
  token,            // Current token (ref)
  isReady,          // Is reCAPTCHA loaded (ref)
  isLoading,        // Is loading (ref)
  error,            // Error message (ref)
  widgetId          // Widget ID (ref)
} = useRecaptcha(config);

Angular Service API

class RecaptchaService {
  token$: Observable<string | null>;
  isReady$: Observable<boolean>;
  isLoading$: Observable<boolean>;
  error$: Observable<string | null>;
  
  async init(config: RecaptchaConfig): Promise<void>;
  render(container: HTMLElement, config?: RecaptchaConfig): number;
  execute(): void;
  reset(): void;
  getResponse(): string;
  async verify(): Promise<VerifyResponse>;
}

Vanilla Class API

class VanillaRecaptcha {
  constructor(config: RecaptchaConfig);
  
  async init(): Promise<void>;
  render(container: HTMLElement | string): number;
  execute(): void;
  reset(): void;
  getResponse(): string;
  async verify(token?: string): Promise<VerifyResponse>;
  getToken(): string | null;
  isReady(): boolean;
  isLoading(): boolean;
  getError(): string | null;
}

๐Ÿ”’ Backend Integration

This SDK is designed to work with a centralized backend verification service:

Verification Request

POST https://api.example.com/captcha/verify

{
  "token": "03AGdBq27...",
  "clientId": "react-app"
}

Verification Response

{
  "success": true,
  "timestamp": "2024-01-01T12:00:00Z",
  "challengeTs": "2024-01-01T11:59:55Z",
  "hostname": "example.com"
}

See the backend service documentation for implementation details.

๐ŸŽจ Invisible reCAPTCHA

For invisible reCAPTCHA (no visible widget):

React

const { containerRef, execute, token } = useInvisibleRecaptcha({
  siteKey: 'your-site-key',
  clientId: 'react-app',
  verifyUrl: 'https://api.example.com/captcha/verify'
});

const handleSubmit = () => {
  execute(); // Triggers the challenge
};

useEffect(() => {
  if (token) {
    // Token received, submit form
  }
}, [token]);

Vue

<script setup>
const { containerRef, execute, token } = useInvisibleRecaptcha({
  siteKey: 'your-site-key',
  clientId: 'vue-app',
  verifyUrl: 'https://api.example.com/captcha/verify'
});

const handleSubmit = () => {
  execute();
};

watch(token, (newToken) => {
  if (newToken) {
    // Token received, submit form
  }
});
</script>

๐Ÿงช Testing

The SDK includes comprehensive unit tests:

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Watch mode
npm run test:watch

๐Ÿ”ง Advanced Usage

Custom Script Loading

import { loadRecaptchaScript, renderRecaptcha } from '@recaptcha/universal-sdk';

// Load script with custom language
await loadRecaptchaScript('es');

// Render widget
const container = document.getElementById('recaptcha');
const widgetId = renderRecaptcha(container, {
  siteKey: 'your-site-key',
  options: { theme: 'dark' }
});

Manual Verification

import { verifyRecaptchaToken } from '@recaptcha/universal-sdk';

const result = await verifyRecaptchaToken('token', {
  siteKey: 'your-site-key',
  verifyUrl: 'https://api.example.com/captcha/verify',
  clientId: 'my-app'
});

if (result.success) {
  console.log('Verification successful');
}

๐ŸŒ SSR Support

The SDK is SSR-safe and will gracefully handle server-side environments:

// Next.js example
'use client';

import { useRecaptcha } from '@recaptcha/universal-sdk/react';

export default function ContactForm() {
  const { containerRef, token } = useRecaptcha({
    siteKey: process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY!,
    clientId: 'nextjs-app',
    verifyUrl: '/api/captcha/verify'
  });

  // Component implementation
}

๐Ÿค Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

๐Ÿ“ License

MIT License - see LICENSE for details.

๐Ÿ’ก Support


Built with โค๏ธ for production use