# Build a custom waitlist form

> This guide is for users who want to build a custom flow. To use a _prebuilt_ UI, use the [Account Portal pages](https://clerk.com/docs/guides/account-portal/overview.md?sdk=js-frontend) or [prebuilt components](https://clerk.com/docs/js-frontend/reference/components/overview.md).

Clerk's [<Waitlist />](https://clerk.com/docs/js-frontend/reference/components/authentication/waitlist.md) component provides an out-of-the-box solution for allowing users to join your waitlist for early access to your app. However, if you're building a custom user interface, you can use the [useWaitlist()](https://clerk.com/docs/reference/hooks/use-waitlist.md) hook to build a custom waitlist form.

This guide demonstrates how to use the Clerk API to build a custom user interface for joining your app's waitlist.

## Before you start

Before using the `useWaitlist()` hook, you must enable **Waitlist** mode in the Clerk Dashboard:

1. In the Clerk Dashboard, navigate to the [**Waitlist**](https://dashboard.clerk.com/~/user-authentication/waitlist) page.
2. Toggle on **Enable waitlist** and select **Save**.

## Build the custom flow

The following example demonstrates how to use the `useWaitlist()` hook to create a custom waitlist form. Users can submit their email address to join the waitlist, and the component displays appropriate feedback based on the submission state.

**index.html**

filename: index.html
```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/clerk.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Clerk + JavaScript App</title>
  </head>

  <body>
    <div id="success-message" style="display: none">
      <h1>Successfully joined the waitlist!</h1>
      <p>We'll notify you when you're approved.</p>
    </div>

    <div id="waitlist-form-container">
      <h1>Join the Waitlist</h1>
      <form id="waitlist-form">
        <label for="email">Email address</label>
        <input id="email" name="emailAddress" type="email" required />
        <p id="error" style="color: red"></p>
        <button type="submit">Join Waitlist</button>
      </form>
    </div>

    <script type="module" src="main.js" async crossorigin="anonymous"></script>
  </body>
</html>
```

**main.js**

filename: main.js
```js
import { Clerk } from '@clerk/clerk-js'

const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY

const clerk = new Clerk(pubKey)
await clerk.load()

const successMessage = document.getElementById('success-message')
const formContainer = document.getElementById('waitlist-form-container')
const form = document.getElementById('waitlist-form')
const errorElement = document.getElementById('error')

// Check if user has already joined the waitlist
if (clerk.waitlist?.id) {
  successMessage.style.display = 'block'
  formContainer.style.display = 'none'
} else {
  form.addEventListener('submit', async (e) => {
    e.preventDefault()
    const formData = new FormData(e.target)
    const emailAddress = formData.get('emailAddress')

    try {
      await clerk.waitlist.join({ emailAddress })
      // Show success message
      successMessage.style.display = 'block'
      formContainer.style.display = 'none'
    } catch (error) {
      // Display error message
      errorElement.textContent = error.errors?.[0]?.longMessage || 'Failed to join waitlist'
    }
  })
}
```

---

## Sitemap

[Overview of all docs pages](https://clerk.com/docs/llms.txt)
