---
title: Embed the widget
description: Embed a Turnstile widget on your website with JavaScript or HTML.
image: https://developers.cloudflare.com/core-services-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/turnstile/llms.txt  
> Use this file to discover all available pages before exploring further.

[Skip to content](#%5Ftop) 

### Tags

[ JavaScript ](https://developers.cloudflare.com/search/?tags=JavaScript)[ SPA ](https://developers.cloudflare.com/search/?tags=SPA) 

# Embed the widget

Learn how to add the Turnstile widget to your webpage using implicit or explicit rendering methods.

Turnstile offers two ways to add widgets to your page. **Implicit rendering** automatically scans your HTML for widget containers when the page loads. **Explicit rendering** gives you programmatic control to create widgets at any time using JavaScript. Use implicit rendering for static pages where forms exist at page load. Use explicit rendering for dynamic content and single-page applications (SPAs) where forms are created after the initial page load.

| Feature                 | Implicit rendering                 | Explicit rendering                 |
| ----------------------- | ---------------------------------- | ---------------------------------- |
| **Ease of setup**       | Simple, minimal code               | Requires additional JavaScript     |
| **Control over timing** | Renders automatically on page load | Full control over rendering timing |
| **Use cases**           | Static content                     | Dynamic or interactive content     |
| **Customization**       | Limited to HTML attributes         | Extensive via JavaScript API       |

## Prerequisites

Before you begin, you must have:

* A Cloudflare account
* [A Turnstile widget](https://developers.cloudflare.com/turnstile/get-started/#1-create-your-widget) with a sitekey
* Access to edit your website's HTML
* Basic knowledge of HTML and JavaScript

## Process

1. Page load: The Turnstile script loads and scans for elements or waits for programmatic calls.
2. Widget rendering: Widgets are created and begin running challenges.
3. Token generation: When a challenge is completed, a token is generated.
4. Form integration: The token is made available via callbacks or hidden form fields.
5. Server validation: Your server receives the token and validates it using the Siteverify API.

## Implicit rendering

Implicit rendering automatically scans your HTML for elements with the `cf-turnstile` class and renders widgets without additional JavaScript code. This set up is ideal for static pages where you want the widget to load immediately when the page loads.

### Use cases

Cloudflare recommends using implicit rendering on the following scenarios:

* You have simple implementations and want a quick integration.
* You have static websites with straightforward forms.
* You want widgets to appear immediately on pageload.
* You do not need programmatic control of the widget.

### Implementation

#### 1\. Add the Turnstile script

**Include the Turnstile Script**: Add the Turnstile JavaScript API to your HTML file within the `<head>` section or just before the closing `</body>` tag.

```

<script

  src="https://challenges.cloudflare.com/turnstile/v0/api.js"

  async

  defer

></script>


```

Warning

The `api.js` file must be fetched from the exact URL shown above. Proxying or caching this file will cause Turnstile to fail when future updates are released.

#### 2\. (Optional) Optimize performance with resource hints

Add resource hints to improve loading performance by establishing early connections to Cloudflare servers. Place this `<link>` tag in your HTML `<head>` section before the Turnstile script.

```

<link rel="preconnect" href="https://challenges.cloudflare.com" />


```

#### 3\. Add widget elements

Add widget containers where you want the challenges to appear on your website.

```

<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>


```

#### 4\. Configure with data attributes

[Customize your widgets](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/widget-configurations/) using data attributes. Insert a `div` element where you want the widget to appear.

```

<div

  class="cf-turnstile"

  data-sitekey="<YOUR-SITE-KEY>"

  data-theme="light"

  data-size="normal"

  data-callback="onSuccess"

></div>


```

Once a challenge has been solved, a token is passed to the success callback. This token must be validated against our [Siteverify endpoint](https://developers.cloudflare.com/turnstile/get-started/server-side-validation/).

### Complete implicit rendering examples by use case

Basic login form

Turnstile is often used to protect forms on websites such as login forms or contact forms. You can embed the widget within your `<form>` tag.

Example

```

<!DOCTYPE html>

<html>

<head>

    <title>Login Form</title>

    <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

</head>

<body>

    <form action="/login" method="POST">

        <input type="text" name="username" placeholder="Username" required />

        <input type="password" name="password" placeholder="Password" required />


        <!-- Turnstile widget with basic configuration -->

        <div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>

        <button type="submit">Log in</button>

    </form>


</body>

</html>


```

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

Complete HTML example

```

<!DOCTYPE html>

<html lang="en">

  <head>

    <meta charset="UTF-8" />

    <title>Implicit Rendering with Cloudflare Turnstile</title>

    <script

      src="https://challenges.cloudflare.com/turnstile/v0/api.js"

      async

      defer

    ></script>

  </head>

  <body>

    <h1>Contact Us</h1>

    <form action="/submit" method="POST">

      <label for="name">Name:</label><br />

      <input type="text" id="name" name="name" required /><br />

      <label for="email">Email:</label><br />

      <input type="email" id="email" name="email" required /><br />

      <!-- Turnstile Widget -->

      <div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>

      <br />

      <button type="submit">Submit</button>

    </form>

  </body>

</html>


```

Advanced form with callbacks

Example

```

<form action="/contact" method="POST" id="contact-form">

  <input type="email" name="email" placeholder="Email" required />

  <textarea name="message" placeholder="Message" required></textarea>

  <!-- Widget with callbacks and custom configuration -->

  <div

    class="cf-turnstile"

    data-sitekey="<YOUR-SITE-KEY>"

    data-theme="auto"

    data-size="flexible"

    data-callback="onTurnstileSuccess"

    data-error-callback="onTurnstileError"

    data-expired-callback="onTurnstileExpired"

  ></div>

  <button type="submit" id="submit-btn" disabled>Send Message</button>

</form>


<script>

  function onTurnstileSuccess(token) {

    console.log("Turnstile success:", token);

    document.getElementById("submit-btn").disabled = false;

  }

  function onTurnstileError(errorCode) {

    console.error("Turnstile error:", errorCode);

    document.getElementById("submit-btn").disabled = true;

  }

  function onTurnstileExpired() {

    console.warn("Turnstile token expired");

    document.getElementById("submit-btn").disabled = true;

  }

</script>


```

Multiple widgets with different configurations

Example

```

<!-- Compact widget for newsletter signup -->

<form action="/newsletter" method="POST">

  <input type="email" name="email" placeholder="Email" />

  <div

    class="cf-turnstile"

    data-sitekey="<YOUR-SITE-KEY>"

    data-size="compact"

    data-action="newsletter"

  ></div>

  <button type="submit">Subscribe</button>

</form>


<!-- Normal widget for contact form -->

<form action="/contact" method="POST">

  <input type="text" name="name" placeholder="Name" />

  <input type="email" name="email" placeholder="Email" />

  <textarea name="message" placeholder="Message"></textarea>

  <div

    class="cf-turnstile"

    data-sitekey="<YOUR-SITE-KEY>"

    data-action="contact"

    data-theme="dark"

  ></div>

  <button type="submit">Send</button>

</form>


```

Automatic form integration

When you embed a Turnstile widget inside a `<form>` element, an invisible input field with the name `cf-turnstile-response` is automatically created. This field contains the verification token and gets submitted with your other form data.

```

<form action="/submit" method="POST">

  <input type="text" name="data" />

  <div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>

  <!-- Hidden field automatically added: -->

  <!-- <input type="hidden" name="cf-turnstile-response" value="TOKEN_VALUE" /> -->

  <button type="submit">Submit</button>

</form>


```

---

## Explicit rendering

Explicit rendering gives you programmatic control over when and where the widget appears and how the widgets are created using JavaScript functions. This method is suitable for dynamic content, single-page applications (SPAs), or conditional rendering based on user interactions.

### Use cases

Cloudflare recommends using explicit rendering on the following scenarios:

* You have dynamic websites and single-page applications (SPAs).
* You need to control the timing of widget creation.
* You want to conditionally render the widget based on visitor interactions.
* You want multiple widgets with different configurations.
* You have complex applications requiring widget lifecycle management.

### Implementation

#### 1\. Add the script to your website with explicit rendering

```

<script

  src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"

  defer

></script>


```

#### 2\. Create container elements

Create containers without the `cf-turnstile` class.

```

<div id="turnstile-container"></div>


```

#### 3\. Render the widgets programmatically

Call `turnstile.render()` when you are ready to create the widget.

JavaScript

```

const widgetId = turnstile.render("#turnstile-container", {

  sitekey: "<YOUR-SITE-KEY>",

  callback: function (token) {

    console.log("Success:", token);

  },

});


```

### Optional calls

After rendering the Turnstile widget explicitly, you may need to interact with it based on your application's requirements. Refer to the sections below to manage the widget's state.

#### Reset a widget

To reset the widget if the given widget timed out or expired, you can use the function:

JavaScript

```

turnstile.reset(widgetId);


```

#### Get the response token

Retrieve the current response token at any time:

JavaScript

```

const responseToken = turnstile.getResponse(widgetId);


```

#### Remove a widget

When a widget is no longer needed, it can be removed from the page using:

JavaScript

```

turnstile.remove(widgetId);


```

This will not call any callback and will remove all related DOM elements.

### Complete explicit rendering examples by use case

Basic explicit implementation

Example

```

<!DOCTYPE html>

<html>

  <head>

    <title>Explicit Rendering</title>

    <script

      src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"

      defer

    ></script>

  </head>

  <body>

    <form id="login-form">

      <input type="text" name="username" placeholder="Username" />

      <input type="password" name="password" placeholder="Password" />

      <div id="turnstile-widget"></div>

      <button type="submit">Login</button>

    </form>


    <script>

      window.onload = function () {

        turnstile.render("#turnstile-widget", {

          sitekey: "<YOUR-SITE-KEY>",

          callback: function (token) {

            console.log("Turnstile token:", token);

            // Handle successful verification

          },

          "error-callback": function (errorCode) {

            console.error("Turnstile error:", errorCode);

          },

        });

      };

    </script>

  </body>

</html>


```

Using onload callback

Example

```

<script

  src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=onTurnstileLoad"

  defer

></script>

<div id="widget-container"></div>

<script>

  function onTurnstileLoad() {

    turnstile.render("#widget-container", {

      sitekey: "<YOUR-SITE-KEY>",

      theme: "light",

      callback: function (token) {

        console.log("Challenge completed:", token);

      },

    });

  }

</script>


```

Advanced SPA implementation

Example

```

<div id="dynamic-form-container"></div>


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


<script>

  class TurnstileManager {

    constructor() {

      this.widgets = new Map();

    }

    createWidget(containerId, config) {

      // Wait for Turnstile to be ready

      turnstile.ready(() => {

        const widgetId = turnstile.render(containerId, {

          sitekey: config.sitekey,

          theme: config.theme || "auto",

          size: config.size || "normal",

          callback: (token) => {

            console.log(`Widget ${widgetId} completed:`, token);

            if (config.onSuccess) config.onSuccess(token, widgetId);

          },

          "error-callback": (error) => {

            console.error(`Widget ${widgetId} error:`, error);

            if (config.onError) config.onError(error, widgetId);

          },

        });


        this.widgets.set(containerId, widgetId);

        return widgetId;

      });

    }

    removeWidget(containerId) {

      const widgetId = this.widgets.get(containerId);

      if (widgetId) {

        turnstile.remove(widgetId);

        this.widgets.delete(containerId);

      }

    }

    resetWidget(containerId) {

      const widgetId = this.widgets.get(containerId);

      if (widgetId) {

        turnstile.reset(widgetId);

      }

    }

  }


  // Usage

  const manager = new TurnstileManager();


  // Create a widget when user clicks a button

  document.getElementById("show-form-btn").addEventListener("click", () => {

    document.getElementById("dynamic-form-container").innerHTML = `

        <form>

            <input type="email" placeholder="Email" />

            <div id="turnstile-widget"></div>

            <button type="submit">Submit</button>

        </form>

    `;

    manager.createWidget("#turnstile-widget", {

      sitekey: "<YOUR-SITE-KEY>",

      theme: "dark",

      onSuccess: (token) => {

        // Handle successful verification

        console.log("Form ready for submission");

      },

    });

  });

</script>


```

### Widget lifecycle management

Explicit rendering provides full control over the widget lifecycle.

JavaScript

```

// Render a widget

const widgetId = turnstile.render("#container", {

  sitekey: "<YOUR-SITE-KEY>",

  callback: handleSuccess,

});


// Get the current token

const token = turnstile.getResponse(widgetId);


// Check if widget is expired

const isExpired = turnstile.isExpired(widgetId);


// Reset the widget (clears current state)

turnstile.reset(widgetId);


// Remove the widget completely

turnstile.remove(widgetId);


```

### Execution mode

Control when challenges run with execution modes.

JavaScript

```

// Render widget but don't run challenge yet

const widgetId = turnstile.render("#container", {

  sitekey: "<YOUR-SITE-KEY>",

  execution: "execute", // Don't auto-execute

});


// Later, run the challenge when needed

turnstile.execute("#container");


```

---

## Performance and user experience optimization

Cloudflare recommends that you execute the Turnstile script as early upon the visitor's page entry as possible, so that the verification is complete and the interaction is available once the visitor attempts an action on the page.

---

## Configuration options

Both implicit and explicit rendering methods support the same configuration options. Refer to the table below for the most commonly used configurations.

| Option         | Description                | Values                            |
| -------------- | -------------------------- | --------------------------------- |
| sitekey        | Your widget's sitekey      | Required string                   |
| theme          | Visual theme               | auto, light, dark                 |
| size           | Widget size                | normal, flexible, compact         |
| callback       | Success callback           | Function                          |
| error-callback | Error callback             | Function                          |
| execution      | When to run the challenge  | render, execute                   |
| appearance     | When the widget is visible | always, execute, interaction-only |

For a complete list of configuration options, refer to [Widget configurations](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/widget-configurations/).

---

## Testing

You can test your Turnstile widget on your webpage without triggering an actual Cloudflare Challenge by using a testing sitekey.

Refer to [Testing](https://developers.cloudflare.com/turnstile/troubleshooting/testing/) for more information.

---

## Limitations

Turnstile is designed to function only on pages using `http://` or `https://` URI schemes. Other protocols, such as `file://`, are not supported for embedding the widget.

---

## Security requirements

* Server-side validation is mandatory. 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/).
* Tokens expire after 300 seconds (5 minutes). Each token can only be validated once. Expired or used tokens must be replaced with fresh challenges.

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/turnstile/","name":"Turnstile"}},{"@type":"ListItem","position":3,"item":{"@id":"/turnstile/get-started/","name":"Get started"}},{"@type":"ListItem","position":4,"item":{"@id":"/turnstile/get-started/client-side-rendering/","name":"Embed the widget"}}]}
```
