--- outline: [2, 3, 4] --- # Widget Cap's client-side widget handles requesting, solving and displaying challenges using a native web component and rust-flavoured WASM. It also includes the [programmatic mode](./programmatic). ## Installation ::: code-group ```sh [pnpm] pnpm add @cap.js/widget ``` ```sh [npm] npm i @cap.js/widget ``` ```sh [bun] bun add @cap.js/widget ``` ```html [cdn] ``` ::: ## Usage The widget requires a `data-cap-api-endpoint` pointing at your Cap deployment. For Standalone instances, this is: ``` https://// ``` ### Vanilla ```html
``` ::: tip When the widget lives inside a `
`, it automatically injects a hidden `cap-token` input, and the token is submitted alongside your other fields with no extra JavaScript needed. ::: ### React ```jsx import "@cap.js/widget"; export default function ContactForm() { return ( console.log("token:", e.detail.token)} onprogress={(e) => console.log(e.detail.progress)} onerror={(e) => console.error(e.detail.message)} /> ); } ``` ::: tip We recommend using React 19 or later as it improves custom element event handling ::: ### Vue ```vue ``` If you're getting an unknown-component warning, add this to your `vite.config.js`: ```js export default defineConfig({ plugins: [ vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith("cap-") }, }, }), ], }); ``` ### Svelte 5 ```svelte
console.log("token:", e.detail.token)} on:progress={(e) => console.log(e.detail.progress)} on:error={(e) => console.error(e.detail.message)} /> ``` ### SolidJS ```jsx import "@cap.js/widget"; export default function ContactForm() { return (
console.log("token:", e.detail.token)} on:progress={(e) => console.log(e.detail.progress)} on:error={(e) => console.error(e.detail.message)} /> ); } ``` ### Astro ```astro --- // ContactForm.astro ---
``` If you're rendering a React/Vue/Svelte component inside Astro, follow that framework's guide above and add `client:load` to the component. ### Preact ```jsx import "@cap.js/widget"; export default function ContactForm() { return (
console.log("token:", e.detail.token)} onprogress={(e) => console.log(e.detail.progress)} onerror={(e) => console.error(e.detail.message)} /> ); } ``` ### Qwik ```tsx import { component$ } from "@builder.io/qwik"; import "@cap.js/widget"; export default component$(() => { return (
console.log("token:", e.detail.token)} on:progress$={(e: CustomEvent) => console.log(e.detail.progress)} on:error$={(e: CustomEvent) => console.error(e.detail.message)} /> ); }); ``` ## Programmatic mode If you don't want a visible widget, for example when protecting a background action like a post submission, use the [programmatic mode](./programmatic): ```js import Cap from "@cap.js/widget"; const cap = new Cap({ apiEndpoint: "https:////", }); const { token } = await cap.solve(); ``` ## Events All events are dispatched as CustomEvent. | Event | When it fires | Detail | | ---------- | ------------------------------ | ---------------------- | | `solve` | Challenge solved successfully | `{ token: string }` | | `progress` | Progress update during solving | `{ progress: number }` | | `error` | An error occurred | `{ message: string }` | | `reset` | Widget reset to initial state | `{}` | ## Options You can also specify a custom fetch function with `window.CAP_CUSTOM_FETCH`: ```js window.CAP_CUSTOM_FETCH = (url, params) => fetch(url, params); ``` You can also set a custom WASM url (for example the Standalone asset server's) with `window.CAP_CUSTOM_WASM_URL` or set a nonce for the CSS with `window.CAP_CSS_NONCE` ### Attributes | Attribute | Description | | ------------------------------ | ----------------------------------------------------------------------------- | | `data-cap-api-endpoint` | **Required.** Your Cap endpoint: `https:////` | | `data-cap-worker-count` | Number of solver workers (defaults to `navigator.hardwareConcurrency \|\| 8`) | | `data-cap-hidden-field-name` | Name of the hidden token input in a `
` (default: `cap-token`) | | `data-cap-troubleshooting-url` | Custom URL for the "Troubleshooting" link shown when a user is blocked | #### i18n All widget labels can be overridden with `data-cap-i18n-*` attributes. These default to English ```html ``` ### Styling Override any of these CSS variables on the `cap-widget` element: ```css cap-widget { --cap-background: #fdfdfd; --cap-border-color: #dddddd8f; --cap-border-radius: 14px; --cap-widget-height: 30px; --cap-widget-width: 230px; --cap-widget-padding: 14px; --cap-gap: 15px; --cap-color: #212121; --cap-checkbox-size: 25px; --cap-checkbox-border: 1px solid #aaaaaad1; --cap-checkbox-border-radius: 6px; --cap-checkbox-background: #fafafa91; --cap-checkbox-margin: 2px; --cap-font: system-ui, -apple-system, sans-serif; --cap-spinner-color: #000; --cap-spinner-background-color: #eee; --cap-spinner-thickness: 5px; } ```