# Registration form template

A free registration form for events, workshops and classes, with ticket type, phone and a terms checkbox. Copy the HTML, CSS and JS, or use it in React or Vue.

This form asks who is coming, how to reach them, which ticket they want and how they heard about you. The terms checkbox is required, and the phone number and source are optional.

Live preview, field editor, four themes and styled versions for every framework: https://nisuform.com/templates/registration-form

## Fields

| Field | Name | Type | Required |
| --- | --- | --- | --- |
| Full name | `name` | Short text | Yes |
| Email | `email` | Email | Yes |
| Phone | `phone` | Phone | No |
| Ticket | `ticket` | Dropdown (General admission, Student, Group of 5 or more) | Yes |
| How did you hear about us? | `source` | Dropdown (Friend or colleague, Social media, Newsletter, Search engine, Other) | No |
| I agree to the event terms and privacy policy | `terms` | Checkbox | Yes |

## How to use it

1. Create a free form at https://app.nisuform.com and copy its form key.
2. Paste the code where the form should appear and replace `YOUR_FORM_KEY` with your key.
3. Submit the form once. The submission appears in your Nisuform inbox and you get an email.

## Styled HTML, CSS and JavaScript

The form with its styles and a small script that sends it without a page reload and shows the success message.

```html
<form id="nf-registration-form" class="nf-form" action="https://api.nisuform.com/s/YOUR_FORM_KEY" method="POST">
  <style>
#nf-registration-form {
  --nf-accent: #e60058;
  --nf-on-accent: #ffffff;
  --nf-text: #171717;
  --nf-muted: #525252;
  --nf-field: #f5f5f5;
  --nf-field-text: #171717;
  --nf-placeholder: #6b6b6b;
  --nf-control: #8a8a8a;
  --nf-error: #b91c1c;
  color-scheme: light;
  box-sizing: border-box;
  width: 100%;
  max-width: 480px;
  margin: 0;
  padding: 28px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 16px;
  background: linear-gradient(180deg, #ffffff, #ffffff);
  box-shadow: 0 16px 40px -16px rgba(0, 0, 0, 0.12);
  color: var(--nf-text);
  font: inherit;
  font-size: 16px;
  line-height: 1.5;
  text-align: left;
}
#nf-registration-form *, #nf-registration-form *::before, #nf-registration-form *::after { box-sizing: border-box; }
#nf-registration-form [hidden] { display: none !important; }
#nf-registration-form .nf-title { margin: 0; font-size: 1.25em; font-weight: 650; line-height: 1.25; letter-spacing: -0.01em; color: var(--nf-text); }
#nf-registration-form .nf-desc { margin: 6px 0 0; font-size: 0.9375em; color: var(--nf-muted); }
#nf-registration-form .nf-fields { display: grid; gap: 18px; margin-top: 22px; }
#nf-registration-form .nf-head + .nf-fields { margin-top: 22px; }
#nf-registration-form > .nf-fields:first-child { margin-top: 0; }
#nf-registration-form .nf-field { margin: 0; padding: 0; border: 0; min-width: 0; }
#nf-registration-form .nf-label { display: block; margin: 0 0 6px; padding: 0; font-size: 0.875em; font-weight: 600; color: var(--nf-text); }
#nf-registration-form .nf-req { color: var(--nf-muted); font-weight: 400; }
#nf-registration-form .nf-help { margin: 6px 0 0; font-size: 0.8125em; color: var(--nf-muted); }
#nf-registration-form .nf-input { display: block; width: 100%; margin: 0; padding: 10px 12px; border: 1px solid transparent; border-radius: 10px; background: var(--nf-field); color: var(--nf-field-text); font: inherit; font-size: 1em; line-height: 1.4; outline: 2px solid transparent; outline-offset: 1px; transition: border-color 150ms, box-shadow 150ms; }
#nf-registration-form input.nf-input, #nf-registration-form select.nf-input { height: 2.75em; padding-top: 0; padding-bottom: 0; }
#nf-registration-form textarea.nf-input { min-height: 112px; padding-top: 10px; padding-bottom: 10px; resize: vertical; }
#nf-registration-form .nf-input::placeholder { color: var(--nf-placeholder); opacity: 1; }
#nf-registration-form .nf-input:hover { border-color: color-mix(in srgb, var(--nf-text) 14%, transparent); }
#nf-registration-form .nf-input:focus { border-color: var(--nf-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-registration-form .nf-input:user-invalid { border-color: var(--nf-error); }
#nf-registration-form .nf-input:user-invalid:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-error) 24%, transparent); }
#nf-registration-form .nf-input:-webkit-autofill { -webkit-text-fill-color: var(--nf-field-text); caret-color: var(--nf-field-text); box-shadow: inset 0 0 0 100px var(--nf-field); }
#nf-registration-form .nf-input:-webkit-autofill:focus { box-shadow: inset 0 0 0 100px var(--nf-field), 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-registration-form input[type="number"].nf-input { -moz-appearance: textfield; appearance: textfield; }
#nf-registration-form input[type="number"].nf-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
#nf-registration-form input[type="number"].nf-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
#nf-registration-form input[type="date"].nf-input { min-width: 0; text-align: left; }
#nf-registration-form input[type="date"].nf-input::-webkit-date-and-time-value { text-align: left; }
#nf-registration-form input[type="date"].nf-input.nf-empty:not(:focus) { color: var(--nf-placeholder); }
#nf-registration-form input[type="date"].nf-input::-webkit-calendar-picker-indicator { margin: 0; padding: 4px; border-radius: 6px; opacity: 0.55; cursor: pointer; transition: opacity 150ms; }
#nf-registration-form input[type="date"].nf-input::-webkit-calendar-picker-indicator:hover { opacity: 1; }
#nf-registration-form select.nf-input { -webkit-appearance: none; appearance: none; padding-right: 40px; background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23525252%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M6%209l6%206l6%20-6%22%2F%3E%3C%2Fsvg%3E"); background-repeat: no-repeat; background-position: right 12px center; background-size: 16px 16px; cursor: pointer; }
#nf-registration-form select.nf-input:has(option[value=""]:checked) { color: var(--nf-placeholder); }
#nf-registration-form select.nf-input option { color: var(--nf-field-text); }
#nf-registration-form .nf-options { display: grid; gap: 8px; }
#nf-registration-form .nf-option { display: flex; align-items: center; gap: 10px; min-height: 2.75em; margin: 0; padding: 10px 12px; border: 1px solid color-mix(in srgb, var(--nf-text) 14%, transparent); border-radius: 10px; background: transparent; color: var(--nf-text); font-size: 0.9375em; line-height: 1.4; cursor: pointer; transition: border-color 150ms, background-color 150ms; }
#nf-registration-form .nf-option:hover { border-color: color-mix(in srgb, var(--nf-text) 28%, transparent); }
#nf-registration-form .nf-option:has(input:checked) { border-color: var(--nf-accent); background: color-mix(in srgb, var(--nf-accent) 8%, transparent); }
#nf-registration-form .nf-option span { min-width: 0; overflow-wrap: anywhere; }
#nf-registration-form .nf-option input { -webkit-appearance: none; appearance: none; flex: none; width: 18px; height: 18px; margin: 0; border: 1.5px solid var(--nf-control); border-radius: 50%; background: transparent; outline: 2px solid transparent; cursor: pointer; transition: border-color 150ms, box-shadow 150ms; }
#nf-registration-form .nf-option input:checked { border-color: var(--nf-accent); background: radial-gradient(circle, var(--nf-on-accent) 0 3px, var(--nf-accent) 3.5px); }
#nf-registration-form .nf-option input:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-registration-form .nf-check { display: flex; align-items: flex-start; gap: 10px; margin: 0; font-size: 0.9375em; line-height: 1.5; color: var(--nf-text); cursor: pointer; }
#nf-registration-form .nf-check input { -webkit-appearance: none; appearance: none; flex: none; width: 20px; height: 20px; margin: 1px 0 0; border: 1.5px solid var(--nf-control); border-radius: 6px; background: var(--nf-field) center / 14px 14px no-repeat; outline: 2px solid transparent; cursor: pointer; transition: background-color 150ms, border-color 150ms, box-shadow 150ms; }
#nf-registration-form .nf-check input:checked { border-color: var(--nf-accent); background-color: var(--nf-accent); background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23ffffff%22%20stroke-width%3D%223.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M5%2012l5%205l10%20-10%22%2F%3E%3C%2Fsvg%3E"); }
#nf-registration-form .nf-check input:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-registration-form .nf-check input:user-invalid { border-color: var(--nf-error); }
#nf-registration-form .nf-rating { display: flex; flex-wrap: wrap; margin: 0 0 0 -4px; }
#nf-registration-form .nf-rating label { display: inline-flex; padding: 4px; border-radius: 8px; color: var(--nf-control); color: color-mix(in srgb, var(--nf-control) 45%, transparent); cursor: pointer; transition: color 150ms; }
#nf-registration-form .nf-rating svg { width: 30px; height: 30px; fill: currentColor; stroke: currentColor; stroke-width: 1.5; stroke-linejoin: round; transition: transform 150ms; }
#nf-registration-form .nf-rating input:checked + label, #nf-registration-form .nf-rating label:has(~ input:checked) { color: var(--nf-accent); }
#nf-registration-form .nf-rating:hover input + label { color: var(--nf-control); color: color-mix(in srgb, var(--nf-control) 45%, transparent); }
#nf-registration-form .nf-rating:hover label:hover, #nf-registration-form .nf-rating:hover label:has(~ label:hover) { color: var(--nf-accent); }
#nf-registration-form .nf-rating label:active svg { transform: scale(0.88); }
#nf-registration-form .nf-rating input:focus-visible + label { outline: 2px solid var(--nf-accent); outline-offset: 0; }
#nf-registration-form .nf-file { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 112px; padding: 16px; border: 1.5px dashed var(--nf-control); border-color: color-mix(in srgb, var(--nf-control) 55%, transparent); border-radius: 10px; background: var(--nf-field); color: var(--nf-muted); text-align: center; transition: border-color 150ms, background-color 150ms, box-shadow 150ms; }
#nf-registration-form .nf-file-input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
#nf-registration-form .nf-file svg { width: 22px; height: 22px; margin-bottom: 4px; }
#nf-registration-form .nf-file-text { margin: 0; max-width: 100%; font-size: 0.875em; overflow-wrap: anywhere; }
#nf-registration-form .nf-file-cta { font-weight: 600; color: var(--nf-text); }
#nf-registration-form .nf-file-types { margin: 0; font-size: 0.75em; color: var(--nf-placeholder); }
#nf-registration-form .nf-file:hover { border-color: var(--nf-control); }
#nf-registration-form .nf-file:focus-within { border-color: var(--nf-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-registration-form .nf-file.nf-file-over { border-color: var(--nf-accent); background: color-mix(in srgb, var(--nf-accent) 8%, var(--nf-field)); }
#nf-registration-form .nf-file.nf-file-chosen { border-style: solid; border-color: var(--nf-accent); }
#nf-registration-form .nf-file.nf-file-chosen .nf-file-text { font-weight: 600; color: var(--nf-text); }
#nf-registration-form .nf-file:has(.nf-file-input:user-invalid) { border-color: var(--nf-error); }
#nf-registration-form .nf-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
#nf-registration-form .nf-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
#nf-registration-form .nf-captcha { margin-top: 18px; }
#nf-registration-form .nf-submit { display: inline-flex; align-items: center; justify-content: center; width: 100%; min-height: 44px; margin: 22px 0 0; padding: 10px 18px; border: 0; border-radius: 10px; background: var(--nf-accent); color: var(--nf-on-accent); font: inherit; font-size: 1em; font-weight: 600; cursor: pointer; transition: filter 150ms, transform 150ms; }
#nf-registration-form .nf-submit:hover { filter: brightness(0.94); }
#nf-registration-form .nf-submit:active { transform: scale(0.98); }
#nf-registration-form .nf-submit:focus-visible { outline: 2px solid var(--nf-accent); outline-offset: 3px; }
#nf-registration-form .nf-submit:disabled { opacity: 0.6; cursor: default; }
#nf-registration-form .nf-error { margin: 14px 0 0; padding: 10px 12px; border-radius: 10px; background: color-mix(in srgb, var(--nf-error) 10%, transparent); font-size: 0.875em; color: var(--nf-error); }
#nf-registration-form.nf-sent { display: flex; flex-direction: column; justify-content: center; min-height: min(var(--nf-height, 0px), 440px); }
#nf-registration-form .nf-success { display: flex; flex-direction: column; align-items: center; padding: 20px 0; text-align: center; outline: none; }
#nf-registration-form .nf-success-icon { display: flex; flex: none; align-items: center; justify-content: center; width: 56px; height: 56px; margin: 0 0 20px; border-radius: 50%; background: var(--nf-accent); color: var(--nf-on-accent); box-shadow: 0 0 0 8px color-mix(in srgb, var(--nf-accent) 12%, transparent); }
#nf-registration-form .nf-success-icon svg { display: block; width: 28px; height: 28px; margin: 0; stroke-dasharray: 1; }
#nf-registration-form .nf-success-title { margin: 0; max-width: 24em; font-size: 1.125em; font-weight: 650; line-height: 1.35; letter-spacing: -0.01em; color: var(--nf-text); text-wrap: balance; overflow-wrap: anywhere; }
#nf-registration-form .nf-success-text { margin: 0; max-width: 30em; font-size: 0.9375em; line-height: 1.5; color: var(--nf-text); text-wrap: pretty; overflow-wrap: anywhere; }
#nf-registration-form .nf-success-title + .nf-success-text { margin-top: 6px; color: var(--nf-muted); }
#nf-registration-form.nf-sent .nf-success { animation: nf-success-in 400ms cubic-bezier(0.16, 1, 0.3, 1) both; }
#nf-registration-form.nf-sent .nf-success-icon { animation: nf-success-pop 500ms cubic-bezier(0.34, 1.56, 0.64, 1) both; }
#nf-registration-form.nf-sent .nf-success-icon path { animation: nf-success-draw 360ms 160ms cubic-bezier(0.65, 0, 0.35, 1) both; }
@keyframes nf-success-in { from { opacity: 0; transform: translateY(6px); } }
@keyframes nf-success-pop { from { transform: scale(0.6); } }
@keyframes nf-success-draw { from { stroke-dashoffset: 1; } }
@media (prefers-reduced-motion: reduce) { #nf-registration-form * { transition: none !important; animation: none !important; } }
  </style>
  <div class="nf-head">
    <h2 class="nf-title">Register now</h2>
    <p class="nf-desc">Save your seat in under a minute.</p>
  </div>
  <div class="nf-fields">
    <div class="nf-field">
      <label class="nf-label" for="nf-registration-form-name">Full name<span class="nf-req" aria-hidden="true"> *</span></label>
      <input class="nf-input" id="nf-registration-form-name" name="name" type="text" autocomplete="name" placeholder="Jane Smith" required>
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-registration-form-email">Email<span class="nf-req" aria-hidden="true"> *</span></label>
      <input class="nf-input" id="nf-registration-form-email" name="email" type="email" autocomplete="email" placeholder="jane@example.com" required>
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-registration-form-phone">Phone</label>
      <input class="nf-input" id="nf-registration-form-phone" name="phone" type="tel" autocomplete="tel" placeholder="+1 555 0100">
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-registration-form-ticket">Ticket<span class="nf-req" aria-hidden="true"> *</span></label>
      <select class="nf-input" id="nf-registration-form-ticket" name="ticket" required>
        <option value="">Choose a ticket</option>
        <option value="General admission">General admission</option>
        <option value="Student">Student</option>
        <option value="Group of 5 or more">Group of 5 or more</option>
      </select>
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-registration-form-source">How did you hear about us?</label>
      <select class="nf-input" id="nf-registration-form-source" name="source">
        <option value="">Choose one</option>
        <option value="Friend or colleague">Friend or colleague</option>
        <option value="Social media">Social media</option>
        <option value="Newsletter">Newsletter</option>
        <option value="Search engine">Search engine</option>
        <option value="Other">Other</option>
      </select>
    </div>
    <div class="nf-field">
      <label class="nf-check"><input type="checkbox" id="nf-registration-form-terms" name="terms" value="yes" required><span>I agree to the event terms and privacy policy<span class="nf-req" aria-hidden="true"> *</span></span></label>
    </div>
  </div>
  <div class="nf-hp" aria-hidden="true"><input type="text" name="_gotcha" tabindex="-1" autocomplete="off"></div>
  <button class="nf-submit" type="submit">Register</button>
  <p class="nf-error" role="alert" hidden></p>
  <div class="nf-success" role="status" tabindex="-1" hidden>
    <span class="nf-success-icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" focusable="false"><path d="M5 12l5 5l10 -10" pathLength="1"/></svg></span>
    <p class="nf-success-title">You’re registered!</p>
    <p class="nf-success-text">See you there.</p>
  </div>
</form>
<script>
(function () {
  var form = document.getElementById("nf-registration-form")
  if (!form || !window.fetch) return
  var started = Date.now()
  var button = form.querySelector('.nf-submit')
  var error = form.querySelector('.nf-error')
  var success = form.querySelector('.nf-success')
  form.addEventListener('submit', function (event) {
    event.preventDefault()
    var data = new FormData(form)
    data.set('_elapsed', String(Date.now() - started))
    var label = button.textContent
    button.disabled = true
    button.textContent = 'Sending...'
    error.hidden = true
    fetch(form.action, {
      method: 'POST',
      headers: { Accept: 'application/json' },
      body: new URLSearchParams(data),
    })
      .then(function (response) {
        return response.json().catch(function () { return {} }).then(function (body) {
          if (!response.ok || !body.ok) throw new Error(body.message || 'Something went wrong. Please try again.')
        })
      })
      .then(function () {
        var redirect = form.getAttribute('data-redirect')
        if (redirect) {
          window.location.href = redirect
          return
        }
        form.style.setProperty('--nf-height', form.offsetHeight + 'px')
        Array.prototype.forEach.call(form.children, function (child) {
          if (child !== success && child.tagName !== 'STYLE') child.hidden = true
        })
        form.classList.add('nf-sent')
        success.hidden = false
        success.focus()
      })
      .catch(function (reason) {
        error.textContent = reason && reason.message ? reason.message : 'Something went wrong. Please try again.'
        error.hidden = false
      })
      .finally(function () {
        button.disabled = false
        button.textContent = label
        if (window.turnstile) window.turnstile.reset()
      })
  })
})()
</script>
```

## Unstyled HTML

Bare markup that picks up your own CSS. It posts without JavaScript and keeps the hidden `_gotcha` field that stops bots.

```html
<form action="https://api.nisuform.com/s/YOUR_FORM_KEY" method="POST">
  <label>
    Full name
    <input name="name" type="text" placeholder="Jane Smith" required>
  </label>
  <label>
    Email
    <input name="email" type="email" placeholder="jane@example.com" required>
  </label>
  <label>
    Phone
    <input name="phone" type="tel" placeholder="+1 555 0100">
  </label>
  <label>
    Ticket
    <select name="ticket" required>
      <option value="" disabled selected>Choose one</option>
      <option value="General admission">General admission</option>
      <option value="Student">Student</option>
      <option value="Group of 5 or more">Group of 5 or more</option>
    </select>
  </label>
  <label>
    How did you hear about us?
    <select name="source">
      <option value="" disabled selected>Choose one</option>
      <option value="Friend or colleague">Friend or colleague</option>
      <option value="Social media">Social media</option>
      <option value="Newsletter">Newsletter</option>
      <option value="Search engine">Search engine</option>
      <option value="Other">Other</option>
    </select>
  </label>
  <label>
    <input name="terms" type="checkbox" value="yes" required>
    I agree to the event terms and privacy policy
  </label>
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Register</button>
</form>
```

## Framework components

### React

```jsx
import { useRef, useState } from 'react'

export function RegistrationForm() {
  const renderedAt = useRef(Date.now())
  const [status, setStatus] = useState('idle')

  async function onSubmit(event) {
    event.preventDefault()
    setStatus('sending')
    const data = Object.fromEntries(new FormData(event.currentTarget))
    data._elapsed = String(Date.now() - renderedAt.current)
    const response = await fetch("https://api.nisuform.com/s/YOUR_FORM_KEY", {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
      body: JSON.stringify(data),
    })
    setStatus(response.ok ? 'sent' : 'error')
  }

  if (status === 'sent') return <p>You’re registered! See you there.</p>

  return (
    <form onSubmit={onSubmit}>
      <label>
        Full name
        <input name="name" type="text" placeholder="Jane Smith" required />
      </label>
      <label>
        Email
        <input name="email" type="email" placeholder="jane@example.com" required />
      </label>
      <label>
        Phone
        <input name="phone" type="tel" placeholder="+1 555 0100" />
      </label>
      <label>
        Ticket
        <select name="ticket" defaultValue="" required>
          <option value="" disabled>Choose one</option>
          <option value="General admission">General admission</option>
          <option value="Student">Student</option>
          <option value="Group of 5 or more">Group of 5 or more</option>
        </select>
      </label>
      <label>
        How did you hear about us?
        <select name="source" defaultValue="">
          <option value="" disabled>Choose one</option>
          <option value="Friend or colleague">Friend or colleague</option>
          <option value="Social media">Social media</option>
          <option value="Newsletter">Newsletter</option>
          <option value="Search engine">Search engine</option>
          <option value="Other">Other</option>
        </select>
      </label>
      <label>
        <input name="terms" type="checkbox" value="yes" required />
        I agree to the event terms and privacy policy
      </label>
      <input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" style={{ display: 'none' }} />
      <button type="submit" disabled={status === 'sending'}>Register</button>
      {status === 'error' && <p role="alert">Something went wrong. Please try again.</p>}
    </form>
  )
}
```

### Next.js

```jsx
'use client'

import { useRef, useState } from 'react'

export function RegistrationForm() {
  const renderedAt = useRef(Date.now())
  const [status, setStatus] = useState('idle')

  async function onSubmit(event) {
    event.preventDefault()
    setStatus('sending')
    const data = Object.fromEntries(new FormData(event.currentTarget))
    data._elapsed = String(Date.now() - renderedAt.current)
    const response = await fetch("https://api.nisuform.com/s/YOUR_FORM_KEY", {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
      body: JSON.stringify(data),
    })
    setStatus(response.ok ? 'sent' : 'error')
  }

  if (status === 'sent') return <p>You’re registered! See you there.</p>

  return (
    <form onSubmit={onSubmit}>
      <label>
        Full name
        <input name="name" type="text" placeholder="Jane Smith" required />
      </label>
      <label>
        Email
        <input name="email" type="email" placeholder="jane@example.com" required />
      </label>
      <label>
        Phone
        <input name="phone" type="tel" placeholder="+1 555 0100" />
      </label>
      <label>
        Ticket
        <select name="ticket" defaultValue="" required>
          <option value="" disabled>Choose one</option>
          <option value="General admission">General admission</option>
          <option value="Student">Student</option>
          <option value="Group of 5 or more">Group of 5 or more</option>
        </select>
      </label>
      <label>
        How did you hear about us?
        <select name="source" defaultValue="">
          <option value="" disabled>Choose one</option>
          <option value="Friend or colleague">Friend or colleague</option>
          <option value="Social media">Social media</option>
          <option value="Newsletter">Newsletter</option>
          <option value="Search engine">Search engine</option>
          <option value="Other">Other</option>
        </select>
      </label>
      <label>
        <input name="terms" type="checkbox" value="yes" required />
        I agree to the event terms and privacy policy
      </label>
      <input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" style={{ display: 'none' }} />
      <button type="submit" disabled={status === 'sending'}>Register</button>
      {status === 'error' && <p role="alert">Something went wrong. Please try again.</p>}
    </form>
  )
}
```

### Vue

```vue
<script setup>
import { ref } from 'vue'

const shownAt = Date.now()
const status = ref('idle')

async function onSubmit(event) {
  status.value = 'sending'
  const data = new FormData(event.target)
  data.set('_elapsed', String(Date.now() - shownAt))
  const response = await fetch("https://api.nisuform.com/s/YOUR_FORM_KEY", {
    method: 'POST',
    headers: { Accept: 'application/json' },
    body: data,
  })
  status.value = response.ok ? 'sent' : 'error'
}
</script>

<template>
  <p v-if="status === 'sent'">You’re registered! See you there.</p>
  <form v-else @submit.prevent="onSubmit">
    <label>
      Full name
      <input name="name" type="text" placeholder="Jane Smith" required>
    </label>
    <label>
      Email
      <input name="email" type="email" placeholder="jane@example.com" required>
    </label>
    <label>
      Phone
      <input name="phone" type="tel" placeholder="+1 555 0100">
    </label>
    <label>
      Ticket
      <select name="ticket" required>
        <option value="" disabled selected>Choose one</option>
        <option value="General admission">General admission</option>
        <option value="Student">Student</option>
        <option value="Group of 5 or more">Group of 5 or more</option>
      </select>
    </label>
    <label>
      How did you hear about us?
      <select name="source">
        <option value="" disabled selected>Choose one</option>
        <option value="Friend or colleague">Friend or colleague</option>
        <option value="Social media">Social media</option>
        <option value="Newsletter">Newsletter</option>
        <option value="Search engine">Search engine</option>
        <option value="Other">Other</option>
      </select>
    </label>
    <label>
      <input name="terms" type="checkbox" value="yes" required>
      I agree to the event terms and privacy policy
    </label>
    <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
    <button type="submit" :disabled="status === 'sending'">Register</button>
    <p v-if="status === 'error'" role="alert">Something went wrong. Please try again.</p>
  </form>
</template>
```

### Svelte

```svelte
<script>
  const shownAt = Date.now()
  let status = $state('idle')

  async function onsubmit(event) {
    event.preventDefault()
    status = 'sending'
    const data = new FormData(event.currentTarget)
    data.set('_elapsed', String(Date.now() - shownAt))
    const response = await fetch("https://api.nisuform.com/s/YOUR_FORM_KEY", {
      method: 'POST',
      headers: { Accept: 'application/json' },
      body: data,
    })
    status = response.ok ? 'sent' : 'error'
  }
</script>

{#if status === 'sent'}
  <p>You’re registered! See you there.</p>
{:else}
  <form {onsubmit}>
    <label>
      Full name
      <input name="name" type="text" placeholder="Jane Smith" required>
    </label>
    <label>
      Email
      <input name="email" type="email" placeholder="jane@example.com" required>
    </label>
    <label>
      Phone
      <input name="phone" type="tel" placeholder="+1 555 0100">
    </label>
    <label>
      Ticket
      <select name="ticket" required>
        <option value="" disabled selected>Choose one</option>
        <option value="General admission">General admission</option>
        <option value="Student">Student</option>
        <option value="Group of 5 or more">Group of 5 or more</option>
      </select>
    </label>
    <label>
      How did you hear about us?
      <select name="source">
        <option value="" disabled selected>Choose one</option>
        <option value="Friend or colleague">Friend or colleague</option>
        <option value="Social media">Social media</option>
        <option value="Newsletter">Newsletter</option>
        <option value="Search engine">Search engine</option>
        <option value="Other">Other</option>
      </select>
    </label>
    <label>
      <input name="terms" type="checkbox" value="yes" required>
      I agree to the event terms and privacy policy
    </label>
    <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
    <button type="submit" disabled={status === 'sending'}>Register</button>
    {#if status === 'error'}
      <p role="alert">Something went wrong. Please try again.</p>
    {/if}
  </form>
{/if}
```

### Astro

```astro
---
const endpoint = "https://api.nisuform.com/s/YOUR_FORM_KEY"
---

<form action={endpoint} method="POST">
  <label>
    Full name
    <input name="name" type="text" placeholder="Jane Smith" required>
  </label>
  <label>
    Email
    <input name="email" type="email" placeholder="jane@example.com" required>
  </label>
  <label>
    Phone
    <input name="phone" type="tel" placeholder="+1 555 0100">
  </label>
  <label>
    Ticket
    <select name="ticket" required>
      <option value="" disabled selected>Choose one</option>
      <option value="General admission">General admission</option>
      <option value="Student">Student</option>
      <option value="Group of 5 or more">Group of 5 or more</option>
    </select>
  </label>
  <label>
    How did you hear about us?
    <select name="source">
      <option value="" disabled selected>Choose one</option>
      <option value="Friend or colleague">Friend or colleague</option>
      <option value="Social media">Social media</option>
      <option value="Newsletter">Newsletter</option>
      <option value="Search engine">Search engine</option>
      <option value="Other">Other</option>
    </select>
  </label>
  <label>
    <input name="terms" type="checkbox" value="yes" required>
    I agree to the event terms and privacy policy
  </label>
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Register</button>
</form>
```

## When to use it

- Conference, meetup and webinar registration
- Workshop, course and class signups
- Sports clubs, camps and community programs
- Volunteer signups

## Customize it

### Close registration when seats run out

Set a maximum number of submissions, like 50, and the form closes itself when it is full.

### Charge for tickets

Nisuform does not take payments. On Pro, redirect people to a checkout link after they register, or send an invoice.

### Confirm each registration

On Pro, auto-replies email each person a confirmation with their own answers, like their name and ticket type.

## Frequently asked questions

### Can I use this as a user registration form with a password?

No. This form signs people up for an event or a class, and each registration is stored as a submission in your inbox. Account signup with passwords needs an authentication service, and you should never collect passwords through a form backend.

### How do I stop registrations after a date?

Set a close date in the form settings. From that moment, the form shows a closed message instead of accepting new registrations.

### Can I see registrations in a spreadsheet?

Yes. Export them as CSV on any plan, or on Pro add every new registration to Google Sheets as it arrives.
