Command Palette

Search for a command to run...

Docs
Cap Widget

A headless, shadcn-compatible React component for CAP.

Overview

Cap is a modern, lightweight, open-source CAPTCHA alternative using SHA-256 proof-of-work.

Unlike traditional CAPTCHAs, Cap:

  • Is fast and unobtrusive
  • Uses no tracking or cookies
  • Uses proof-of-work instead of annoying visual puzzles
  • Is fully accessible and self-hostable

Installation

pnpm dlx shadcn@latest add "https://ui.ednesdayw.com/r/cap-widget.json"

Usage

import { CapWidget } from "@/components/ui/cap-widget"
 
const Page = () => {
  const [token, setToken] = useState<string | null>(null);
 
  return (
    <CapWidget
      endpoint="https://captcha.gurl.eu.org/api/"
      onSolve={(token) => {
        console.log("Verification successful:", token);
        setToken(token);
      }}
      onError={(message) => {
        console.error("Verification failed:", message);
      }}
    />
  );
};

Examples

Basic Implementation

Custom Locale

API Reference

Props

PropTypeDefaultDescription
endpointstringRequiredThe CAP API endpoint URL for verification
workerCountnumberundefinedNumber of Web Workers to use for parallel processing
onSolve(token: string) => voidundefinedCallback when verification succeeds
onError(message: string) => voidundefinedCallback when verification fails
onReset() => voidundefinedCallback when widget is reset
onProgress(progress: number) => voidundefinedCallback for verification progress (0-100)
overrideobjectundefinedOverride default CAP configuration
localeobjectundefinedCustom localization strings

Override Configuration

The override prop allows customization of CAP's internal behavior:

<CapWidget
  endpoint="https://captcha.gurl.eu.org/api/"
  override={{
    wasmUrl: "https://custom-cdn.com/cap.wasm",
    customFetch: async (url, options) => {
      // Custom fetch implementation
      return fetch(url, {
        ...options,
        headers: {
          ...options?.headers,
          'X-Custom-Header': 'value'
        }
      });
    }
  }}
/>
PropertyTypeDescription
wasmUrlstringCustom URL for the CAP WebAssembly module
customFetchfunctionCustom fetch function for API requests

Locale Configuration

Customize all text displayed by the widget:

<CapWidget
  endpoint="https://captcha.gurl.eu.org/api/"
  locale={{
    initial: "I'm not a robot",
    verifying: "Verifying...",
    solved: "Verified ✓",
    error: "Verification failed",
    wasmDisabled: "WebAssembly is disabled",
    verifyingAria: "Verifying that you are human",
    solvedAria: "Verification successful",
    errorAria: "Verification failed, please try again"
  }}
/>
PropertyTypeDescription
initialstringInitial state text
verifyingstringText shown during verification
solvedstringText shown when verified
errorstringText shown on error
wasmDisabledstringMessage when WebAssembly is unavailable
verifyingAriastringARIA label during verification
solvedAriastringARIA label when solved
errorAriastringARIA label on error

Styling

The component automatically adapts to your application's theme using CSS variables:

/* Automatically mapped from your theme */
--cap-background: var(--background)
--cap-border-color: var(--border)
--cap-border-radius: var(--radius)
--cap-color: var(--foreground)
--cap-checkbox-border: 1px solid var(--ring)
--cap-checkbox-background: var(--secondary)
--cap-spinner-color: var(--primary)
--cap-spinner-background-color: var(--primary-foreground)
--cap-border-radius: 14px;
--cap-widget-height: 30px;
--cap-widget-width: 230px;
--cap-widget-padding: 14px;
--cap-gap: 15px;
--cap-checkbox-size: 25px;
--cap-checkbox-border-radius: 6px;
--cap-checkbox-margin: 2px;
--cap-font: system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco",
    "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif;
--cap-spinner-thickness: 5px;
--cap-checkmark: url("data:image/svg+xml,...");
--cap-error-cross: url("data:image/svg+xml,...");
--cap-credits-font-size: 12px;
--cap-opacity-hover: 0.8;

You can override these styles using the style prop:

<CapWidget
  endpoint="https://captcha.gurl.eu.org/api/"
  style={{
    "--cap-border-radius": "12px",
    "--cap-spinner-color": "#10b981"
  }}
/>

Events and Callbacks

The component provides comprehensive event handling:

const MyForm = () => {
  const [isVerified, setIsVerified] = useState(false);
  const [progress, setProgress] = useState(0);
 
  return (
    <form>
      <CapWidget
        endpoint="https://captcha.gurl.eu.org/api/"
        onSolve={(token) => {
          setIsVerified(true);
          // Submit form with token
          submitForm(token);
        }}
        onError={(message) => {
          console.error("CAP Error:", message);
          setIsVerified(false);
        }}
        onReset={() => {
          setIsVerified(false);
          setProgress(0);
        }}
        onProgress={(value) => {
          setProgress(value);
        }}
      />
 
      {progress > 0 && !isVerified && (
        <div>Verification progress: {progress}%</div>
      )}
 
      <button type="submit" disabled={!isVerified}>
        Submit
      </button>
    </form>
  );
};

Server-Side Verification

Community Worker Available: If you don't want to deploy your own CAP verification service, you can use the community-hosted worker at https://captcha.gurl.eu.org/ for testing and production use. This is a free service maintained by the community.

After receiving the token from the client, verify it on your backend:

// Node.js example
async function verifyToken(token) {
  const response = await fetch('https://captcha.gurl.eu.org/api/verify', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ token })
  });
 
  const result = await response.json();
  return result.success;
}
 
// Usage in API route
app.post('/submit-form', async (req, res) => {
  const { token, ...formData } = req.body;
 
  const isValid = await verifyToken(token);
  if (!isValid) {
    return res.status(400).json({ error: 'Invalid verification token' });
  }
 
  // Process validated form submission
  // ...
});

Performance Optimization

Web Workers

Control the number of Web Workers for optimal performance:

<CapWidget
  endpoint="https://captcha.gurl.eu.org/api/"
  workerCount={navigator.hardwareConcurrency || 8}
/>

Custom WASM

Use a custom WASM module to reduce latency:

<CapWidget
  endpoint="https://captcha.gurl.eu.org/api/"
  override={{
    wasmUrl: "https://your-cdn.com/cap/cap.wasm"
  }}
/>