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
Prop | Type | Default | Description |
---|---|---|---|
endpoint | string | Required | The CAP API endpoint URL for verification |
workerCount | number | undefined | Number of Web Workers to use for parallel processing |
onSolve | (token: string) => void | undefined | Callback when verification succeeds |
onError | (message: string) => void | undefined | Callback when verification fails |
onReset | () => void | undefined | Callback when widget is reset |
onProgress | (progress: number) => void | undefined | Callback for verification progress (0-100) |
override | object | undefined | Override default CAP configuration |
locale | object | undefined | Custom 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'
}
});
}
}}
/>
Property | Type | Description |
---|---|---|
wasmUrl | string | Custom URL for the CAP WebAssembly module |
customFetch | function | Custom 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"
}}
/>
Property | Type | Description |
---|---|---|
initial | string | Initial state text |
verifying | string | Text shown during verification |
solved | string | Text shown when verified |
error | string | Text shown on error |
wasmDisabled | string | Message when WebAssembly is unavailable |
verifyingAria | string | ARIA label during verification |
solvedAria | string | ARIA label when solved |
errorAria | string | ARIA 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"
}}
/>