# Survey form template

A free survey form with dropdown, multiple choice, number and comment questions. Copy the HTML, CSS and JS, or use it in React, Next.js, Vue, Svelte or Astro.

This survey form uses every common question type in one place: an optional name and email, a number, a dropdown, a multiple choice question and an open comment box. Swap the questions for your own and keep the structure.

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

## Fields

| Field | Name | Type | Required |
| --- | --- | --- | --- |
| Name | `name` | Short text | No |
| Email | `email` | Email | No |
| Age | `age` | Number | No |
| Which best describes you? | `role` | Dropdown (Student, Full-time job, Part-time job, Freelancer, Prefer not to say) | Yes |
| Would you recommend us to a friend? | `recommend` | Multiple choice (Definitely, Maybe, Not sure) | Yes |
| What should we improve? | `improve` | Long text | No |

## 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-survey-form" class="nf-form" action="https://api.nisuform.com/s/YOUR_FORM_KEY" method="POST">
  <style>
#nf-survey-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-survey-form *, #nf-survey-form *::before, #nf-survey-form *::after { box-sizing: border-box; }
#nf-survey-form [hidden] { display: none !important; }
#nf-survey-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-survey-form .nf-desc { margin: 6px 0 0; font-size: 0.9375em; color: var(--nf-muted); }
#nf-survey-form .nf-fields { display: grid; gap: 18px; margin-top: 22px; }
#nf-survey-form .nf-head + .nf-fields { margin-top: 22px; }
#nf-survey-form > .nf-fields:first-child { margin-top: 0; }
#nf-survey-form .nf-field { margin: 0; padding: 0; border: 0; min-width: 0; }
#nf-survey-form .nf-label { display: block; margin: 0 0 6px; padding: 0; font-size: 0.875em; font-weight: 600; color: var(--nf-text); }
#nf-survey-form .nf-req { color: var(--nf-muted); font-weight: 400; }
#nf-survey-form .nf-help { margin: 6px 0 0; font-size: 0.8125em; color: var(--nf-muted); }
#nf-survey-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-survey-form input.nf-input, #nf-survey-form select.nf-input { height: 2.75em; padding-top: 0; padding-bottom: 0; }
#nf-survey-form textarea.nf-input { min-height: 112px; padding-top: 10px; padding-bottom: 10px; resize: vertical; }
#nf-survey-form .nf-input::placeholder { color: var(--nf-placeholder); opacity: 1; }
#nf-survey-form .nf-input:hover { border-color: color-mix(in srgb, var(--nf-text) 14%, transparent); }
#nf-survey-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-survey-form .nf-input:user-invalid { border-color: var(--nf-error); }
#nf-survey-form .nf-input:user-invalid:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-error) 24%, transparent); }
#nf-survey-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-survey-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-survey-form input[type="number"].nf-input { -moz-appearance: textfield; appearance: textfield; }
#nf-survey-form input[type="number"].nf-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
#nf-survey-form input[type="number"].nf-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
#nf-survey-form input[type="date"].nf-input { min-width: 0; text-align: left; }
#nf-survey-form input[type="date"].nf-input::-webkit-date-and-time-value { text-align: left; }
#nf-survey-form input[type="date"].nf-input.nf-empty:not(:focus) { color: var(--nf-placeholder); }
#nf-survey-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-survey-form input[type="date"].nf-input::-webkit-calendar-picker-indicator:hover { opacity: 1; }
#nf-survey-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-survey-form select.nf-input:has(option[value=""]:checked) { color: var(--nf-placeholder); }
#nf-survey-form select.nf-input option { color: var(--nf-field-text); }
#nf-survey-form .nf-options { display: grid; gap: 8px; }
#nf-survey-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-survey-form .nf-option:hover { border-color: color-mix(in srgb, var(--nf-text) 28%, transparent); }
#nf-survey-form .nf-option:has(input:checked) { border-color: var(--nf-accent); background: color-mix(in srgb, var(--nf-accent) 8%, transparent); }
#nf-survey-form .nf-option span { min-width: 0; overflow-wrap: anywhere; }
#nf-survey-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-survey-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-survey-form .nf-option input:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-survey-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-survey-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-survey-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-survey-form .nf-check input:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-survey-form .nf-check input:user-invalid { border-color: var(--nf-error); }
#nf-survey-form .nf-rating { display: flex; flex-wrap: wrap; margin: 0 0 0 -4px; }
#nf-survey-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-survey-form .nf-rating svg { width: 30px; height: 30px; fill: currentColor; stroke: currentColor; stroke-width: 1.5; stroke-linejoin: round; transition: transform 150ms; }
#nf-survey-form .nf-rating input:checked + label, #nf-survey-form .nf-rating label:has(~ input:checked) { color: var(--nf-accent); }
#nf-survey-form .nf-rating:hover input + label { color: var(--nf-control); color: color-mix(in srgb, var(--nf-control) 45%, transparent); }
#nf-survey-form .nf-rating:hover label:hover, #nf-survey-form .nf-rating:hover label:has(~ label:hover) { color: var(--nf-accent); }
#nf-survey-form .nf-rating label:active svg { transform: scale(0.88); }
#nf-survey-form .nf-rating input:focus-visible + label { outline: 2px solid var(--nf-accent); outline-offset: 0; }
#nf-survey-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-survey-form .nf-file-input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
#nf-survey-form .nf-file svg { width: 22px; height: 22px; margin-bottom: 4px; }
#nf-survey-form .nf-file-text { margin: 0; max-width: 100%; font-size: 0.875em; overflow-wrap: anywhere; }
#nf-survey-form .nf-file-cta { font-weight: 600; color: var(--nf-text); }
#nf-survey-form .nf-file-types { margin: 0; font-size: 0.75em; color: var(--nf-placeholder); }
#nf-survey-form .nf-file:hover { border-color: var(--nf-control); }
#nf-survey-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-survey-form .nf-file.nf-file-over { border-color: var(--nf-accent); background: color-mix(in srgb, var(--nf-accent) 8%, var(--nf-field)); }
#nf-survey-form .nf-file.nf-file-chosen { border-style: solid; border-color: var(--nf-accent); }
#nf-survey-form .nf-file.nf-file-chosen .nf-file-text { font-weight: 600; color: var(--nf-text); }
#nf-survey-form .nf-file:has(.nf-file-input:user-invalid) { border-color: var(--nf-error); }
#nf-survey-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-survey-form .nf-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
#nf-survey-form .nf-captcha { margin-top: 18px; }
#nf-survey-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-survey-form .nf-submit:hover { filter: brightness(0.94); }
#nf-survey-form .nf-submit:active { transform: scale(0.98); }
#nf-survey-form .nf-submit:focus-visible { outline: 2px solid var(--nf-accent); outline-offset: 3px; }
#nf-survey-form .nf-submit:disabled { opacity: 0.6; cursor: default; }
#nf-survey-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-survey-form.nf-sent { display: flex; flex-direction: column; justify-content: center; min-height: min(var(--nf-height, 0px), 440px); }
#nf-survey-form .nf-success { display: flex; flex-direction: column; align-items: center; padding: 20px 0; text-align: center; outline: none; }
#nf-survey-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-survey-form .nf-success-icon svg { display: block; width: 28px; height: 28px; margin: 0; stroke-dasharray: 1; }
#nf-survey-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-survey-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-survey-form .nf-success-title + .nf-success-text { margin-top: 6px; color: var(--nf-muted); }
#nf-survey-form.nf-sent .nf-success { animation: nf-success-in 400ms cubic-bezier(0.16, 1, 0.3, 1) both; }
#nf-survey-form.nf-sent .nf-success-icon { animation: nf-success-pop 500ms cubic-bezier(0.34, 1.56, 0.64, 1) both; }
#nf-survey-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-survey-form * { transition: none !important; animation: none !important; } }
  </style>
  <div class="nf-head">
    <h2 class="nf-title">Quick survey</h2>
    <p class="nf-desc">Six questions, about two minutes. Thanks for helping out.</p>
  </div>
  <div class="nf-fields">
    <div class="nf-field">
      <label class="nf-label" for="nf-survey-form-name">Name</label>
      <input class="nf-input" id="nf-survey-form-name" name="name" type="text" autocomplete="name" placeholder="Jane Smith">
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-survey-form-email">Email</label>
      <input class="nf-input" id="nf-survey-form-email" name="email" type="email" autocomplete="email" placeholder="jane@example.com">
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-survey-form-age">Age</label>
      <input class="nf-input" id="nf-survey-form-age" name="age" type="number" inputmode="decimal" min="13" max="120" placeholder="30">
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-survey-form-role">Which best describes you?<span class="nf-req" aria-hidden="true"> *</span></label>
      <select class="nf-input" id="nf-survey-form-role" name="role" required>
        <option value="">Choose one</option>
        <option value="Student">Student</option>
        <option value="Full-time job">Full-time job</option>
        <option value="Part-time job">Part-time job</option>
        <option value="Freelancer">Freelancer</option>
        <option value="Prefer not to say">Prefer not to say</option>
      </select>
    </div>
    <fieldset class="nf-field nf-group">
      <legend class="nf-label">Would you recommend us to a friend?<span class="nf-req" aria-hidden="true"> *</span></legend>
      <div class="nf-options">
        <label class="nf-option"><input type="radio" name="recommend" value="Definitely" required><span>Definitely</span></label>
        <label class="nf-option"><input type="radio" name="recommend" value="Maybe"><span>Maybe</span></label>
        <label class="nf-option"><input type="radio" name="recommend" value="Not sure"><span>Not sure</span></label>
      </div>
    </fieldset>
    <div class="nf-field">
      <label class="nf-label" for="nf-survey-form-improve">What should we improve?</label>
      <textarea class="nf-input" id="nf-survey-form-improve" name="improve" rows="4"></textarea>
    </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">Submit survey</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">Thanks for taking the survey!</p>
  </div>
</form>
<script>
(function () {
  var form = document.getElementById("nf-survey-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>
    Name
    <input name="name" type="text" placeholder="Jane Smith">
  </label>
  <label>
    Email
    <input name="email" type="email" placeholder="jane@example.com">
  </label>
  <label>
    Age
    <input name="age" type="number" min="13" max="120" placeholder="30">
  </label>
  <label>
    Which best describes you?
    <select name="role" required>
      <option value="" disabled selected>Choose one</option>
      <option value="Student">Student</option>
      <option value="Full-time job">Full-time job</option>
      <option value="Part-time job">Part-time job</option>
      <option value="Freelancer">Freelancer</option>
      <option value="Prefer not to say">Prefer not to say</option>
    </select>
  </label>
  <fieldset>
    <legend>Would you recommend us to a friend?</legend>
    <label><input type="radio" name="recommend" value="Definitely" required> Definitely</label>
    <label><input type="radio" name="recommend" value="Maybe"> Maybe</label>
    <label><input type="radio" name="recommend" value="Not sure"> Not sure</label>
  </fieldset>
  <label>
    What should we improve?
    <textarea name="improve" rows="5"></textarea>
  </label>
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Submit survey</button>
</form>
```

## Framework components

### React

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

export function SurveyForm() {
  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>Thanks for taking the survey!</p>

  return (
    <form onSubmit={onSubmit}>
      <label>
        Name
        <input name="name" type="text" placeholder="Jane Smith" />
      </label>
      <label>
        Email
        <input name="email" type="email" placeholder="jane@example.com" />
      </label>
      <label>
        Age
        <input name="age" type="number" min="13" max="120" placeholder="30" />
      </label>
      <label>
        Which best describes you?
        <select name="role" defaultValue="" required>
          <option value="" disabled>Choose one</option>
          <option value="Student">Student</option>
          <option value="Full-time job">Full-time job</option>
          <option value="Part-time job">Part-time job</option>
          <option value="Freelancer">Freelancer</option>
          <option value="Prefer not to say">Prefer not to say</option>
        </select>
      </label>
      <fieldset>
        <legend>Would you recommend us to a friend?</legend>
        <label><input type="radio" name="recommend" value="Definitely" required /> Definitely</label>
        <label><input type="radio" name="recommend" value="Maybe" /> Maybe</label>
        <label><input type="radio" name="recommend" value="Not sure" /> Not sure</label>
      </fieldset>
      <label>
        What should we improve?
        <textarea name="improve" rows={5}></textarea>
      </label>
      <input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" style={{ display: 'none' }} />
      <button type="submit" disabled={status === 'sending'}>Submit survey</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 SurveyForm() {
  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>Thanks for taking the survey!</p>

  return (
    <form onSubmit={onSubmit}>
      <label>
        Name
        <input name="name" type="text" placeholder="Jane Smith" />
      </label>
      <label>
        Email
        <input name="email" type="email" placeholder="jane@example.com" />
      </label>
      <label>
        Age
        <input name="age" type="number" min="13" max="120" placeholder="30" />
      </label>
      <label>
        Which best describes you?
        <select name="role" defaultValue="" required>
          <option value="" disabled>Choose one</option>
          <option value="Student">Student</option>
          <option value="Full-time job">Full-time job</option>
          <option value="Part-time job">Part-time job</option>
          <option value="Freelancer">Freelancer</option>
          <option value="Prefer not to say">Prefer not to say</option>
        </select>
      </label>
      <fieldset>
        <legend>Would you recommend us to a friend?</legend>
        <label><input type="radio" name="recommend" value="Definitely" required /> Definitely</label>
        <label><input type="radio" name="recommend" value="Maybe" /> Maybe</label>
        <label><input type="radio" name="recommend" value="Not sure" /> Not sure</label>
      </fieldset>
      <label>
        What should we improve?
        <textarea name="improve" rows={5}></textarea>
      </label>
      <input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" style={{ display: 'none' }} />
      <button type="submit" disabled={status === 'sending'}>Submit survey</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'">Thanks for taking the survey!</p>
  <form v-else @submit.prevent="onSubmit">
    <label>
      Name
      <input name="name" type="text" placeholder="Jane Smith">
    </label>
    <label>
      Email
      <input name="email" type="email" placeholder="jane@example.com">
    </label>
    <label>
      Age
      <input name="age" type="number" min="13" max="120" placeholder="30">
    </label>
    <label>
      Which best describes you?
      <select name="role" required>
        <option value="" disabled selected>Choose one</option>
        <option value="Student">Student</option>
        <option value="Full-time job">Full-time job</option>
        <option value="Part-time job">Part-time job</option>
        <option value="Freelancer">Freelancer</option>
        <option value="Prefer not to say">Prefer not to say</option>
      </select>
    </label>
    <fieldset>
      <legend>Would you recommend us to a friend?</legend>
      <label><input type="radio" name="recommend" value="Definitely" required> Definitely</label>
      <label><input type="radio" name="recommend" value="Maybe"> Maybe</label>
      <label><input type="radio" name="recommend" value="Not sure"> Not sure</label>
    </fieldset>
    <label>
      What should we improve?
      <textarea name="improve" rows="5"></textarea>
    </label>
    <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
    <button type="submit" :disabled="status === 'sending'">Submit survey</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>Thanks for taking the survey!</p>
{:else}
  <form {onsubmit}>
    <label>
      Name
      <input name="name" type="text" placeholder="Jane Smith">
    </label>
    <label>
      Email
      <input name="email" type="email" placeholder="jane@example.com">
    </label>
    <label>
      Age
      <input name="age" type="number" min="13" max="120" placeholder="30">
    </label>
    <label>
      Which best describes you?
      <select name="role" required>
        <option value="" disabled selected>Choose one</option>
        <option value="Student">Student</option>
        <option value="Full-time job">Full-time job</option>
        <option value="Part-time job">Part-time job</option>
        <option value="Freelancer">Freelancer</option>
        <option value="Prefer not to say">Prefer not to say</option>
      </select>
    </label>
    <fieldset>
      <legend>Would you recommend us to a friend?</legend>
      <label><input type="radio" name="recommend" value="Definitely" required> Definitely</label>
      <label><input type="radio" name="recommend" value="Maybe"> Maybe</label>
      <label><input type="radio" name="recommend" value="Not sure"> Not sure</label>
    </fieldset>
    <label>
      What should we improve?
      <textarea name="improve" rows="5"></textarea>
    </label>
    <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
    <button type="submit" disabled={status === 'sending'}>Submit survey</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>
    Name
    <input name="name" type="text" placeholder="Jane Smith">
  </label>
  <label>
    Email
    <input name="email" type="email" placeholder="jane@example.com">
  </label>
  <label>
    Age
    <input name="age" type="number" min="13" max="120" placeholder="30">
  </label>
  <label>
    Which best describes you?
    <select name="role" required>
      <option value="" disabled selected>Choose one</option>
      <option value="Student">Student</option>
      <option value="Full-time job">Full-time job</option>
      <option value="Part-time job">Part-time job</option>
      <option value="Freelancer">Freelancer</option>
      <option value="Prefer not to say">Prefer not to say</option>
    </select>
  </label>
  <fieldset>
    <legend>Would you recommend us to a friend?</legend>
    <label><input type="radio" name="recommend" value="Definitely" required> Definitely</label>
    <label><input type="radio" name="recommend" value="Maybe"> Maybe</label>
    <label><input type="radio" name="recommend" value="Not sure"> Not sure</label>
  </fieldset>
  <label>
    What should we improve?
    <textarea name="improve" rows="5"></textarea>
  </label>
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Submit survey</button>
</form>
```

## When to use it

- Customer and user research
- Event and course feedback with more detail than a rating
- Market research before a launch
- Learning HTML forms with every common input type in one styled example

## Customize it

### Let people pick several answers

Use checkboxes that share a name ending in `[]`, like `topics[]`. Nisuform saves the checked values as one comma-separated answer.

### Keep answers anonymous

Remove the name and email fields and turn off IP address storage for the form.

### Read the results

Export answers as CSV for a spreadsheet or pivot table. Forms built in the dashboard also get answer summaries in Analytics on Pro.

## Frequently asked questions

### How do I make a survey form in HTML?

Wrap your questions in a form element. Use a select for dropdowns, radio buttons that share a name for multiple choice and a textarea for open questions. Then set the form action to a form backend like Nisuform, so answers are saved without a server.

### Can I see survey results as charts?

On Pro, Analytics summarizes dropdown, multiple choice, checkbox, rating and number answers for forms built in the dashboard. On every plan, you can export the answers as CSV and chart them in a spreadsheet.

### How many questions can a survey have?

Up to 50 fields per submission, with up to 10,000 characters per answer.
