---
title: Client-side rendering · Cloudflare Turnstile docs
description: You can initialize and customize the Turnstile widget on your web page via implicit or explicit rendering.
lastUpdated: 2025-02-04T17:18:18.000Z
source_url:
  html: https://29ff08e0.preview.developers.cloudflare.com/turnstile/get-started/client-side-rendering/index.md
  md: https://29ff08e0.preview.developers.cloudflare.com/turnstile/get-started/client-side-rendering/
---

You can initialize and customize the Turnstile widget on your web page via implicit or explicit rendering.

Warning

`api.js` must be fetched from the exact URL stated below. Proxying or caching this file will result in Turnstile failing when future updates are released.

## Implicitly render the Turnstile widget

Note

Refer to the [demo](https://demo.turnstile.workers.dev/) and its [source code](https://github.com/cloudflare/turnstile-demo-workers/blob/main/src/implicit.html).

The HTML is scanned for elements that have a `cf-turnstile` class name:

```html
<div
  class="cf-turnstile"
  data-sitekey="yourSitekey"
  data-callback="javascriptCallback"
></div>
```

Once a challenge has been solved, a token is passed to the success callback. This token must be validated against our siteverify endpoint. A token can only be validated once and cannot be consumed twice.

Warning

It is critical to enforce Turnstile tokens with the siteverify API. The Turnstile token could be invalid, expired, or already redeemed. Not verifying the token will leave major vulnerabilities in your implementation.

You **must** call siteverify to complete your Turnstile configuration. Otherwise, it is incomplete and will result in zeroes for token validation when viewing your metrics in [Turnstile Analytics](https://developers.cloudflare.com/turnstile/turnstile-analytics/).

Note

Once a token has been issued, it can be validated within the next 300 seconds. After 300 seconds, the token is no longer valid and another challenge needs to be solved.

To configure the challenge, refer to [Configurations](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#configurations) containing data attributes and render parameters.

### Protect forms

Turnstile is often used to protect forms on websites such as login forms, contact forms, and more. After inserting the JavaScript script tag, you can embed `<div class="cf-turnstile"></div>` into your site to protect your forms.

```html
<form action="/login" method="POST">
  <input type="text" placeholder="username" />
  <input type="password" placeholder="password" />
  <div class="cf-turnstile" data-sitekey="<YOUR_SITE_KEY>"></div>
  <button type="submit" value="Submit">Log in</button>
</form>
```

An invisible input with the name `cf-turnstile-response` is added and will be sent to the server with the other fields.

Note

A form is not protected by having a widget rendered. The corresponding token that is a result of a widget being rendered also needs to be verified using the siteverify API.

### Disable implicit rendering

You can disable implicit rendering by replacing the script from:

```txt
https://challenges.cloudflare.com/turnstile/v0/api.js
```

To:

```txt
https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit
```

When using `render=explicit`, HTML elements with the `cf-turnstile` class will not show a challenge. The `turnstile.render` function must be invoked using the following steps. To combine both options, pass a query string of `?render=explicit&onload=onloadTurnstileCallback`:

```txt
https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=onloadTurnstileCallback
```

## Explicitly render the Turnstile widget

Note

Refer to the [demo](https://demo.turnstile.workers.dev/explicit) and its [source code](https://github.com/cloudflare/turnstile-demo-workers/blob/main/src/explicit.html).

1. Insert the JavaScript tag and the related code below. Ensure that you have renamed the class name of `.cf-turnstile` to `#example-container` (if you do not set the `render=explicit` query string option as shown above, it will still render otherwise). Once the script is embedded, you will have access to a global function with multiple callback options you can customize. For the following function to work properly, the page must contain an HTML element with ID `example-container`.

```html
<script
  src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback"
  defer
></script>
```

```javascript
window.onloadTurnstileCallback = function () {
  turnstile.render("#example-container", {
    sitekey: "<YOUR_SITE_KEY>",
    callback: function (token) {
      console.log(`Challenge Success ${token}`);
    },
  });
};
```

Or:

```html
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script>
```

```javascript
// if using synchronous loading, will be called once the DOM is ready
turnstile.ready(function () {
  turnstile.render("#example-container", {
    sitekey: "<YOUR_SITE_KEY>",
    callback: function (token) {
      console.log(`Challenge Success ${token}`);
    },
  });
});
```

Turnstile can be programmatically loaded by invoking the `turnstile.render()` function in the global `window` object.

The `turnstile.render: function (container: string | HTMLElement, params: RenderParameters)` render takes an argument to a HTML widget.

If the invocation is successful, the function returns a `widgetId (string)`. If the invocation is unsuccessful, the function returns `undefined`.

## Access a widget's state

In addition to the `render()` function, Turnstile supports obtaining the widget's response from a `widgetId` via the `turnstile.getResponse(widgetId: string)` function. If you omit the `widgetId`, `turnstile.getResponse()` returns the response from the last created widget.

After some time, a widget may become expired and needs to be refreshed (by calling `turnstile.reset(widgetId: string)`). If a widget has expired, `turnstile.getResponse()` will still return the last response, but the response will no longer be valid because it has expired.

You can check if a widget has expired by either subscribing to the `expired-callback` or using the `turnstile.isExpired(widgetId: string)` function, which returns `true` if the widget is expired. If you omit `widgetId`, `turnstile.isExpired()` returns whether the last created widget is expired or not.

## Reset a widget

If a given widget has timed out, expired or needs to be reloaded, you can use the `turnstile.reset(widgetId: string)` function to reset the widget.

## Remove a widget

Once a widget is no longer needed, it can be removed from the page using `turnstile.remove(widgetId: string)`. This will not call any callback and will remove all related DOM elements.

To unmount Turnstile, `turnstile.render()` will return an ID which you can pass to `turnstile.remove()`.

## Refresh an expired token

A few seconds before a token expires, the `expired-callback` is invoked.

The `refresh-expired` or `data-refresh-expired` parameter defines the behavior when the token of a Turnstile widget has expired.

By default, the parameter is set to `auto`, which will automatically instruct Turnstile to obtain a new token by rerunning the challenge. After the challenge is solved again, the `callback`, if specified, is invoked with the new token.

The visitor can also be instructed to manually obtain a new token by setting the `refresh-expired` parameter to `manual`.

Additionally, specifying `never` will not result in a regeneration of a token, and the application using Turnstile will be responsible for obtaining a novel Turnstile token.

## Refresh a timed-out widget

When managed mode is chosen, Turnstile may present the visitor with an interactive challenge at times. If this interactive challenge is presented but was not solved within a given time period, it will time out and Turnstile's challenge process will need to be restarted.

The `refresh-timeout` or `data-refresh-timeout` parameter defines the behavior when the interactive challenge encounters a timeout. By default, the widget automatically refreshes (`auto`). However, the widget can also be configured such that the visitor needs to manually refresh a timed-out widget (`manual`), or the widget can only refreshed externally (`refresh-timeout="never"`) by the application (e.g. by calling Turnstile's `reset()` function).

When a widget is encountering the interactivity timeout the `timeout-callback` is invoked.

## Execution modes

By default, Turnstile tokens are obtained for a visitor upon the rendering of a widget (even in invisible mode). However, in some scenarios, an application may want to embed Turnstile, but defer running the challenge until a certain point in time. This is where execution mode can be used to control when a challenge runs and a token is being generated.

There are two options:

* The challenge runs automatically after calling the `render()` function.

* The challenge runs after the `render()` function has been called, by invoking the `turnstile.execute(container: string | HTMLElement, jsParams?: RenderParameters)` function separately.

  This detaches the appearance and rendering of a widget from its execution.

## Appearance modes

If a widget is visible, its appearance can be controlled via the `appearance` parameter.

By default, `appearance` is set to `always` for visible widget types. However, if `appearance` is set to `execute`, the widget will only become visible after the challenge begins. This is helpful in situations where `execute()` is called after `render()`. If `appearance` is set to `interaction-only`, the widget will become only visible in cases where an interaction is required.

Refer to [Widget types](https://developers.cloudflare.com/turnstile/concepts/widget/) to see widgets by type and state.

## Widget size

The Turnstile widget can have two different fixed sizes or a flexible width size when using the Managed or Non-interactive modes:

| Size | Width | Height |
| - | - | - |
| Normal | 300px | 65px |
| Flexible | 100% (min: 300px) | 65px |
| Compact | 150px | 140px |

```html
<div style="display: block; flex-flow: row;">
  <div
    class="cf-turnstile"
    data-sitekey="1x00000000000000000000AA"
    data-size="flexible"
  ></div>
</div>
```

```html
<div style="display: block; flex-flow: row;">
  <div
    class="cf-turnstile"
    data-sitekey="1x00000000000000000000AA"
    data-size="compact"
  ></div>
</div>
```

## Configurations

| JavaScript Render Parameters | Data Attribute | Description |
| - | - | - |
| `sitekey` | `data-sitekey` | Every widget has a sitekey. This sitekey is associated with the corresponding widget configuration and is created upon the widget creation. |
| `action` | `data-action` | A customer value that can be used to differentiate widgets under the same sitekey in analytics and which is returned upon validation. This can only contain up to 32 alphanumeric characters including `_` and `-`. |
| `cData` | `data-cdata` | A customer payload that can be used to attach customer data to the challenge throughout its issuance and which is returned upon validation. This can only contain up to 255 alphanumeric characters including `_` and `-`. |
| `callback` | `data-callback` | A JavaScript callback invoked upon success of the challenge. The callback is passed a token that can be validated. |
| `error-callback` | `data-error-callback` | A JavaScript callback invoked when there is an error (e.g. network error or the challenge failed). Refer to [Client-side errors](https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/). |
| `execution` | `data-execution` | Execution controls when to obtain the token of the widget and can be on `render` (default) or on `execute`. Refer to [Execution Modes](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#execution-modes) for more information. |
| `expired-callback` | `data-expired-callback` | A JavaScript callback invoked when the token expires and does not reset the widget. |
| `before-interactive-callback` | `data-before-interactive-callback` | A JavaScript callback invoked before the challenge enters interactive mode. |
| `after-interactive-callback` | `data-after-interactive-callback` | A JavaScript callback invoked when challenge has left interactive mode. |
| `unsupported-callback` | `data-unsupported-callback` | A JavaScript callback invoked when a given client/browser is not supported by Turnstile. |
| `theme` | `data-theme` | The widget theme. Can take the following values: `light`, `dark`, `auto`. The default is `auto`, which respects the user preference. This can be forced to light or dark by setting the theme accordingly. |
| `language` | `data-language` | Language to display, must be either: `auto` (default) to use the language that the visitor has chosen, or an ISO 639-1 two-letter language code (e.g. `en`) or language and country code (e.g. `en-US`). Refer to the [list of supported languages](https://developers.cloudflare.com/turnstile/reference/supported-languages/) for more information. |
| `tabindex` | `data-tabindex` | The tabindex of Turnstile's iframe for accessibility purposes. The default value is `0`. |
| `timeout-callback` | `data-timeout-callback` | A JavaScript callback invoked when the challenge presents an interactive challenge but was not solved within a given time. A callback will reset the widget to allow a visitor to solve the challenge again. |
| `response-field` | `data-response-field` | A boolean that controls if an input element with the response token is created, defaults to `true`. |
| `response-field-name` | `data-response-field-name` | Name of the input element, defaults to `cf-turnstile-response`. |
| `size` | `data-size` | The widget size. Can take the following values: `normal`, `flexible`, `compact`. |
| `retry` |  `data-retry` | Controls whether the widget should automatically retry to obtain a token if it did not succeed. The default is `auto`, which will retry automatically. This can be set to `never` to disable retry on failure. |
| `retry-interval` |  `data-retry-interval` | When `retry` is set to `auto`, `retry-interval` controls the time between retry attempts in milliseconds. Value must be a positive integer less than `900000`, defaults to `8000`. |
| `refresh-expired` |  `data-refresh-expired` | Automatically refreshes the token when it expires. Can take `auto`, `manual`, or `never`, defaults to `auto`. |
| `refresh-timeout` | `data-refresh-timeout` | Controls whether the widget should automatically refresh upon entering an interactive challenge and observing a timeout. Can take `auto` (automatically refreshes upon encountering an interactive timeout), `manual` (prompts the visitor to manually refresh) or `never` (will show a timeout), defaults to `auto`. Only applies to widgets of mode managed. |
| `appearance` | `data-appearance` | Appearance controls when the widget is visible. It can be `always` (default), `execute`, or `interaction-only`. Refer to [Appearance modes](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#appearance-modes) for more information. |
| `feedback-enabled` | `data-feedback-enabled` | Allows Cloudflare to gather visitor feedback upon widget failure. It can be `true` (default) or `false`. |
