diff --git a/docs/guide/index.md b/docs/guide/index.md
index 55319b2..86ce86d 100644
--- a/docs/guide/index.md
+++ b/docs/guide/index.md
@@ -12,32 +12,25 @@ We've found that Cap offers a better balance for site admins than big-tech alter
Cap consists of a client-side widget, which solves challenges and displays the checkbox, and a server-side component, which generates challenges and redeems solutions.
-## Integration
-
-### Widget
-
-In order for your users to be able to solve Cap challenges, you'll need to install the widget library to either use the invisible mode or add the checkbox custom component, which looks like this:
-
+## Widget
+
+In order for your users to be able to solve Cap challenges, you'll need to install the widget library to either use the [invisible mode](./invisible.md) or add the checkbox component.
+
+You can find example snippets for multiple frameworks on the [widget docs](./widget.md#usage). We're gonna assume a basic vanilla implementation here for simplicity
+
Add the following to your website's HTML:
```html
-
+
+
```
-::: tip Using the scripts without a CDN
-
-Using `cdn.jsdelivr.net` is optional. If preferred, you can self-host the scripts by either [downloading the latest release files](https://cdn.jsdelivr.net/npm/@cap.js/widget@latest) or, if you're using a framework, installing them with `npm add @cap.js/widget` and serving them from your own server. Remember to update these regularly.
-
-`cdn.jsdelivr.net` is blocked in some jurisdictions, like some parts of China. If your website needs to be reachable from these jurisdictions, we recommend that you self-host the scripts.
-
-:::
-
Next, you can either add the widget component directly to your code:
```html
-
+
```
You'll need to start a server with the Cap API running at the same URL as specified in the `data-cap-api-endpoint` attribute. We'll tell you how to set this up in the next section.
@@ -54,13 +47,13 @@ widget.addEventListener("solve", function (e) {
});
```
-Alternatively, you can wrap the widget in a `
` (where Cap will automatically submit the token alongside other form data as `cap-token`.
+Alternatively, you can wrap the widget in a ``, where Cap will automatically submit the token alongside other form data as `cap-token`.
-You can also use get a token programmatically without displaying the widget by using the invisible mode:
+You can also use get a token programmatically without displaying the widget by using the [invisible mode](./invisible.md):
```js
const cap = new Cap({
- apiEndpoint: "/api/",
+ apiEndpoint: "https:////",
});
const solution = await cap.solve();
diff --git a/docs/guide/widget.md b/docs/guide/widget.md
index c8825e1..3899be9 100644
--- a/docs/guide/widget.md
+++ b/docs/guide/widget.md
@@ -1,117 +1,300 @@
-# @cap.js/widget
+---
+outline: [2, 3, 4]
+---
-[](https://www.jsdelivr.com/package/npm/@cap.js/wasm)
+# Widget
-`@cap.js/widget` is Cap's client-side library. It includes the `cap-widget` web component, the invisible mode and the CAPTCHA solver. First, add it to your client-side code:
+Cap's client-side widget handles requesting, solving and displaying challenges using a native web component and rust-flavoured WASM. It also includes the [invisible mode](./invisible).
+
+## Installation
::: code-group
-```html[jsdelivr]
-
-
-
+```sh [pnpm]
+pnpm add @cap.js/widget
```
-```html[unpkg]
-
-
-
+```sh [npm]
+npm i @cap.js/widget
```
-```js[bundler]
-// npm: npm i @cap.js/widget
-// pnpm: pnpm install @cap.js/widget
-// bun: bun add @cap.js/widget
-
-import Cap from '@cap.js/widget';
-
-// ...
+```sh [bun]
+bun add @cap.js/widget
```
-```html[standalone server]
-
-
+```html [cdn]
+
+
+
```
:::
-You can now use the `` component in your HTML.
+## Usage
-```html
-
+The widget requires a `data-cap-api-endpoint` pointing at your Cap deployment. For Standalone instances, this is:
+
+```
+https:////
```
-**Note:** You'll need to start a server with an API exposing the Cap methods running at the same URL as specified in the `data-cap-api-endpoint` attribute.
+### Vanilla
-The following attributes are supported:
+```html
+
-- `data-cap-api-endpoint`: API endpoint (required if not using custom fetch)
-- `data-cap-worker-count`: Number of workers to use (defaults to `navigator.hardwareConcurrency || 8`)
-- `data-cap-troubleshooting-url`: Custom URL for the "Troubleshooting" link shown when verification is blocked
-- `onsolve=""`: Event listener for the `solve` event
-- [i18n attributes](#i18n)
+
+```
+
+::: tip
+
+When the widget lives inside a `
+ );
+}
+```
+
+::: 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
-const widget = document.querySelector("#cap");
-
-widget.addEventListener("solve", function (e) {
- const token = e.detail.token;
-
- // handle the token as needed
+export default defineConfig({
+ plugins: [
+ vue({
+ template: {
+ compilerOptions: { isCustomElement: (tag) => tag.startsWith("cap-") },
+ },
+ }),
+ ],
});
```
-Alternatively, you can use `onsolve=""` directly within the widget or wrap the widget in a `` (where Cap will automatically submit the token alongside other form data. for this, it'll create a hidden field with name set to its `data-cap-hidden-field-name` attribute or `cap-token`).
+### Svelte 5
+
+```svelte
+
+
+
+```
+
+### SolidJS
+
+```jsx
+import "@cap.js/widget";
+
+export default function ContactForm() {
+ return (
+
+ );
+}
+```
+
+### 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 (
+
+ );
+}
+```
+
+### Qwik
+
+```tsx
+import { component$ } from "@builder.io/qwik";
+import "@cap.js/widget";
+
+export default component$(() => {
+ return (
+
+ );
+});
+```
## Invisible mode
-You can use `new Cap({ ... })` in your client-side JavaScript to create a new Cap instance and use the `solve()` method to solve the challenge. This is helpful for situations where you don't want the Cap widget to be visible but still want security, e.g. on a social media app when posting something.
+If you don't want a visible widget, for example when protecting a background action like a post submission, use the [programmatic API](./invisible):
```js
import Cap from "@cap.js/widget";
const cap = new Cap({
- apiEndpoint: "/api/cap/",
+ apiEndpoint: "https:////",
});
-const token = await cap.solve();
+const { token } = await cap.solve();
```
-## Supported events
+## Events
-The following custom events are supported:
+All events are dispatched as CustomEvent.
-- `solve`: Triggered when the token is generated.
-- `error`: Triggered when an error occurs.
-- `reset`: Triggered when the widget is reset.
-- `progress`: Triggered when there's a progress update while in verification.
+| 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 | `{}` |
-## i18n
+## Options
-You can change the text on each label of the widget by setting the `data-cap-i18n-*` attribute, like this:
+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 `