# Add Wicket to your site

> Put the Wicket script and widget on a page, send the token with the form, check it on your server, and cover explicit rendering, several widgets and apps.

Source: https://www.coritan.com/docs/wicket/add-wicket-to-your-site/

Wicket has two halves. A script on your page draws the widget and puts a token in your form, and your server asks Wicket whether the token is real. This page covers the page half and points to the server half. If you used Cloudflare Turnstile before, you change three things: the script address, the class name `cf-turnstile` to `wicket-widget`, and the verify address ([Move from Cloudflare Turnstile to Wicket](/docs/wicket/migrate-from-turnstile/)).

## Before you begin

- You need a widget, with its site key (it starts with `wk_`) and its secret key (it starts with `wks_`). In the dashboard, go to **Security** and on the **Wicket** card select **Open Wicket**. The [Wicket overview](/docs/wicket/) says what a widget is, and [Manage widgets with the API](/docs/wicket/manage-widgets-with-the-api/) adds one from your own tools.
- The widget must list the hostname of the page that shows it. A hostname covers every name under it, so `example.com` covers `www.example.com`.
- Serve the page over HTTPS. The widget needs a browser feature that browsers give a frame only when the page that holds it is served over HTTPS, or from `localhost`. On a plain `http://` page the widget shows **This browser cannot run the check**.
- To try the page before you use a real widget, use a [test key](/docs/wicket/test-keys/). It works on any hostname, `localhost` included.

## Add the widget to a page

1. Add the script to the page once. It loads with `async defer`, so it does not hold the page up:

   ```html
   <script src="https://wicket.coritan.com/v1/api.js" async defer></script>
   ```

2. Add a `div` with the class `wicket-widget` inside the form, where the check should appear. Its `data-sitekey` is the widget's site key:

   ```html
   <form action="/signup" method="post">
     <label for="email">Email address</label>
     <input id="email" name="email" type="email">
     <div class="wicket-widget" data-sitekey="wk_..." style="min-height: 65px"></div>
     <button type="submit">Create account</button>
   </form>
   ```

3. Load the page. The script draws one widget in every element with the class `wicket-widget`.

The `min-height` keeps the page from moving when the widget appears. The script runs after the page has drawn, so without it the form jumps down by the widget's height. Use `65px` for the Normal and Flexible sizes and `140px` for Compact ([Reserve the widget's height](#reserve-the-widgets-height)). A container for an Invisible widget takes no room: see [Invisible widgets](#invisible-widgets).

### Reserve the widget's height {#reserve-the-widgets-height}

A widget is `65px` high at the Normal and Flexible sizes and `140px` high at the Compact size, whether the size comes from `data-size`, from `wicket.render` or from the widget's **Appearance** setting. Reserve the height of the size the widget uses.

The `style` attribute above is an inline style. When your Content-Security-Policy forbids inline styles (it has `style-src` without `'unsafe-inline'`), the browser ignores the attribute and the page moves. Put the rule in a stylesheet your policy allows instead, and leave the `style` attribute off the container:

```css title="In your stylesheet"
.wicket-widget {
  min-height: 65px;
}

.wicket-widget[data-size="compact"] {
  min-height: 140px;
}
```

The second rule needs `data-size="compact"` on the container. When you set the size in `wicket.render` or in the widget's settings, give the container a class of your own and size that class:

```css
.signup-check {
  min-height: 140px;
}
```

### Options

Put an option on the `div` as a `data-` attribute: `data-theme="dark"`, `data-size="compact"`, `data-action="signup"`. [Widget options](/docs/wicket/widget-options/) lists every option, its values and its default, and the callbacks the widget calls.

## Send the token with the form

When the visitor passes, the script adds a hidden field named `wicket-response` to the widget's container and puts the token in it. Because the container is inside the form, the browser sends the token with the form like any other field.

- To send the form from your own JavaScript, read the token with `wicket.getResponse()`, or pass a `callback` to receive it as soon as it exists.
- To use another field name, set `data-response-field-name`.
- A token works once and for 300 seconds. After you send a form, call `wicket.reset()` to get a new token for the next one. When a visitor takes longer than 300 seconds the widget renews the token on its own.

## Check the token on your server

Never accept the form on the strength of the widget alone, because a script can send a form without loading the widget. Your server posts the token and your secret key to the verify address, and refuses the form when the answer's `success` is `false`.

[Verify a token on your server](/docs/wicket/verify-tokens/) has the call, examples in five languages, the answer and its error codes.

## Draw the widget yourself

Use explicit rendering when the page builds its forms in JavaScript, or when you want to choose when the widget appears.

1. Add `?render=explicit` to the script address. Add `&onload=onWicketLoad` to be called when the script is ready:

   ```html
   <script src="https://wicket.coritan.com/v1/api.js?render=explicit&onload=onWicketLoad" async defer></script>
   ```

2. Define the function the script calls, and render a widget in an element or a selector:

   ```js
   function onWicketLoad() {
     const id = wicket.render("#signup-check", {
       sitekey: "wk_...",
       callback: (token) => console.log("Passed", token),
     });
   }
   ```

   `wicket.render` returns the widget's id. The options you pass override the container's `data-` attributes.

## Put several widgets on one page

Every container with the class `wicket-widget`, and every call to `wicket.render`, gets its own frame, its own token and its own hidden field.

- Give each widget in the same form its own `data-response-field-name`. Two fields with the same name reach your server as two values, and most servers read only one of them.
- Pass the id that `wicket.render` returned, the container element or a selector to `wicket.getResponse`, `wicket.reset` and `wicket.remove`. With no argument they act on the first widget on the page.
- Widgets on one page can use different site keys, themes and sizes.

## Use Wicket in a single-page app

A single-page app builds and removes forms without loading a new page. Load the script once, in your page's `index.html`, with `?render=explicit&onload=onWicketLoad`. Then draw the widget when the form appears and remove it when the form goes away:

```js
// Resolves once the script is ready, whether it has loaded yet or not.
function wicketReady() {
  return new Promise((resolve) => {
    if (window.wicket) window.wicket.ready(resolve);
    else window.onWicketLoad = resolve;
  });
}

// React, in a component that holds the form
useEffect(() => {
  let id;
  let gone = false;
  wicketReady().then(() => {
    if (gone) return;
    id = window.wicket.render(containerRef.current, {
      sitekey: "wk_...",
      callback: setToken,
      "expired-callback": () => setToken(null),
    });
  });
  return () => {
    gone = true;
    if (id !== undefined) window.wicket.remove(id);
  };
}, []);
```

- Call `wicket.remove(id)` when the form is removed, so the frame and its timers go with it.
- Call `wicket.reset(id)` after each submit that fails, so the visitor gets a new token. A token that your server has verified once cannot be used again.
- Keep the token in your app's state with `callback` and `expired-callback`, or read it with `wicket.getResponse(id)` when you submit.
- Give the container its `min-height` in your stylesheet ([Reserve the widget's height](#reserve-the-widgets-height)), so the page does not move when a route draws the widget.

## Invisible widgets

An Invisible widget draws nothing. The script cannot tell that a widget is invisible until its frame has loaded, so a widget left to the default appearance first holds the room of a visible one, and then gives it back, which moves the page. Say it on the container instead, and do not give it a `min-height`:

```html
<div id="check" class="wicket-widget" data-sitekey="wk_..." data-appearance="interaction-only" data-execution="execute"></div>
```

With `data-execution="execute"` the check starts when your code calls `wicket.execute("#check")`, for example when the visitor presses the submit button. [Widget modes](/docs/wicket/widget-modes/) explains the three modes, and the script writes one line to the browser's console when it finds an Invisible widget that was not marked.

## Allow Wicket in a Content Security Policy

If your site sends a Content-Security-Policy, allow the script and the frame the script draws. Add Wicket's address to `script-src` and `frame-src`:

```text
script-src https://wicket.coritan.com;
frame-src https://wicket.coritan.com;
```

- If your policy has no `frame-src`, the browser falls back to `child-src` and then to `default-src`, so name the address in whichever one your policy sets.
- The script sets the frame's size from JavaScript, so it needs no `'unsafe-inline'` in `style-src`. The `style="min-height: 65px"` attribute on your container is your own inline style, and a policy that forbids inline styles ignores it. Put the `min-height` in a stylesheet instead ([Reserve the widget's height](#reserve-the-widgets-height)).
- The frame makes its own requests to Wicket, under its own policy. Your `connect-src` does not need Wicket's address.

## Result

A visitor who passes sees **Success** in the widget, and your form sends a `wicket-response` field. Your server's verify call answers `"success": true`, with the hostname of your page.

## Troubleshooting

[Troubleshoot Wicket](/docs/wicket/troubleshooting/) lists what each message in the widget means, and what to do when the widget does not appear, when your server refuses a token and when a visitor cannot pass.

## Related

- [How Wicket works](/docs/wicket/how-wicket-works/)
- [Widget options](/docs/wicket/widget-options/)
- [Widget modes](/docs/wicket/widget-modes/)
- [Install a widget](/docs/wicket/install-a-widget/)

## Next steps

- [Verify a token on your server](/docs/wicket/verify-tokens/) so your server checks every form.
- [Test keys](/docs/wicket/test-keys/) to try the page and your server without a real widget.
- [Move from Cloudflare Turnstile to Wicket](/docs/wicket/migrate-from-turnstile/) if the page uses Turnstile now.
