# Order form template

A free order form with product, quantity, delivery address and notes. Copy the HTML, CSS and JS, or use it in React, Next.js, Vue, Svelte or Astro.

This form collects the product, the quantity and a delivery address. It suits shops that confirm each order and arrange payment themselves, like bakeries, makers and pre-orders.

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

## Fields

| Field | Name | Type | Required |
| --- | --- | --- | --- |
| Name | `name` | Short text | Yes |
| Email | `email` | Email | Yes |
| Phone | `phone` | Phone | No |
| Product | `product` | Dropdown (Sourdough loaf, Box of 6 pastries, Celebration cake) | Yes |
| Quantity | `quantity` | Number | Yes |
| Delivery address | `address` | Long text | Yes |
| Notes | `notes` | 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-order-form" class="nf-form" action="https://api.nisuform.com/s/YOUR_FORM_KEY" method="POST">
  <style>
#nf-order-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-order-form *, #nf-order-form *::before, #nf-order-form *::after { box-sizing: border-box; }
#nf-order-form [hidden] { display: none !important; }
#nf-order-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-order-form .nf-desc { margin: 6px 0 0; font-size: 0.9375em; color: var(--nf-muted); }
#nf-order-form .nf-fields { display: grid; gap: 18px; margin-top: 22px; }
#nf-order-form .nf-head + .nf-fields { margin-top: 22px; }
#nf-order-form > .nf-fields:first-child { margin-top: 0; }
#nf-order-form .nf-field { margin: 0; padding: 0; border: 0; min-width: 0; }
#nf-order-form .nf-label { display: block; margin: 0 0 6px; padding: 0; font-size: 0.875em; font-weight: 600; color: var(--nf-text); }
#nf-order-form .nf-req { color: var(--nf-muted); font-weight: 400; }
#nf-order-form .nf-help { margin: 6px 0 0; font-size: 0.8125em; color: var(--nf-muted); }
#nf-order-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-order-form input.nf-input, #nf-order-form select.nf-input { height: 2.75em; padding-top: 0; padding-bottom: 0; }
#nf-order-form textarea.nf-input { min-height: 112px; padding-top: 10px; padding-bottom: 10px; resize: vertical; }
#nf-order-form .nf-input::placeholder { color: var(--nf-placeholder); opacity: 1; }
#nf-order-form .nf-input:hover { border-color: color-mix(in srgb, var(--nf-text) 14%, transparent); }
#nf-order-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-order-form .nf-input:user-invalid { border-color: var(--nf-error); }
#nf-order-form .nf-input:user-invalid:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-error) 24%, transparent); }
#nf-order-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-order-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-order-form input[type="number"].nf-input { -moz-appearance: textfield; appearance: textfield; }
#nf-order-form input[type="number"].nf-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
#nf-order-form input[type="number"].nf-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
#nf-order-form input[type="date"].nf-input { min-width: 0; text-align: left; }
#nf-order-form input[type="date"].nf-input::-webkit-date-and-time-value { text-align: left; }
#nf-order-form input[type="date"].nf-input.nf-empty:not(:focus) { color: var(--nf-placeholder); }
#nf-order-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-order-form input[type="date"].nf-input::-webkit-calendar-picker-indicator:hover { opacity: 1; }
#nf-order-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-order-form select.nf-input:has(option[value=""]:checked) { color: var(--nf-placeholder); }
#nf-order-form select.nf-input option { color: var(--nf-field-text); }
#nf-order-form .nf-options { display: grid; gap: 8px; }
#nf-order-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-order-form .nf-option:hover { border-color: color-mix(in srgb, var(--nf-text) 28%, transparent); }
#nf-order-form .nf-option:has(input:checked) { border-color: var(--nf-accent); background: color-mix(in srgb, var(--nf-accent) 8%, transparent); }
#nf-order-form .nf-option span { min-width: 0; overflow-wrap: anywhere; }
#nf-order-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-order-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-order-form .nf-option input:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-order-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-order-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-order-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-order-form .nf-check input:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-order-form .nf-check input:user-invalid { border-color: var(--nf-error); }
#nf-order-form .nf-rating { display: flex; flex-wrap: wrap; margin: 0 0 0 -4px; }
#nf-order-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-order-form .nf-rating svg { width: 30px; height: 30px; fill: currentColor; stroke: currentColor; stroke-width: 1.5; stroke-linejoin: round; transition: transform 150ms; }
#nf-order-form .nf-rating input:checked + label, #nf-order-form .nf-rating label:has(~ input:checked) { color: var(--nf-accent); }
#nf-order-form .nf-rating:hover input + label { color: var(--nf-control); color: color-mix(in srgb, var(--nf-control) 45%, transparent); }
#nf-order-form .nf-rating:hover label:hover, #nf-order-form .nf-rating:hover label:has(~ label:hover) { color: var(--nf-accent); }
#nf-order-form .nf-rating label:active svg { transform: scale(0.88); }
#nf-order-form .nf-rating input:focus-visible + label { outline: 2px solid var(--nf-accent); outline-offset: 0; }
#nf-order-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-order-form .nf-file-input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
#nf-order-form .nf-file svg { width: 22px; height: 22px; margin-bottom: 4px; }
#nf-order-form .nf-file-text { margin: 0; max-width: 100%; font-size: 0.875em; overflow-wrap: anywhere; }
#nf-order-form .nf-file-cta { font-weight: 600; color: var(--nf-text); }
#nf-order-form .nf-file-types { margin: 0; font-size: 0.75em; color: var(--nf-placeholder); }
#nf-order-form .nf-file:hover { border-color: var(--nf-control); }
#nf-order-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-order-form .nf-file.nf-file-over { border-color: var(--nf-accent); background: color-mix(in srgb, var(--nf-accent) 8%, var(--nf-field)); }
#nf-order-form .nf-file.nf-file-chosen { border-style: solid; border-color: var(--nf-accent); }
#nf-order-form .nf-file.nf-file-chosen .nf-file-text { font-weight: 600; color: var(--nf-text); }
#nf-order-form .nf-file:has(.nf-file-input:user-invalid) { border-color: var(--nf-error); }
#nf-order-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-order-form .nf-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
#nf-order-form .nf-captcha { margin-top: 18px; }
#nf-order-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-order-form .nf-submit:hover { filter: brightness(0.94); }
#nf-order-form .nf-submit:active { transform: scale(0.98); }
#nf-order-form .nf-submit:focus-visible { outline: 2px solid var(--nf-accent); outline-offset: 3px; }
#nf-order-form .nf-submit:disabled { opacity: 0.6; cursor: default; }
#nf-order-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-order-form.nf-sent { display: flex; flex-direction: column; justify-content: center; min-height: min(var(--nf-height, 0px), 440px); }
#nf-order-form .nf-success { display: flex; flex-direction: column; align-items: center; padding: 20px 0; text-align: center; outline: none; }
#nf-order-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-order-form .nf-success-icon svg { display: block; width: 28px; height: 28px; margin: 0; stroke-dasharray: 1; }
#nf-order-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-order-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-order-form .nf-success-title + .nf-success-text { margin-top: 6px; color: var(--nf-muted); }
#nf-order-form.nf-sent .nf-success { animation: nf-success-in 400ms cubic-bezier(0.16, 1, 0.3, 1) both; }
#nf-order-form.nf-sent .nf-success-icon { animation: nf-success-pop 500ms cubic-bezier(0.34, 1.56, 0.64, 1) both; }
#nf-order-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-order-form * { transition: none !important; animation: none !important; } }
  </style>
  <div class="nf-head">
    <h2 class="nf-title">Place an order</h2>
    <p class="nf-desc">Order by noon for next-day delivery. We’ll email you to confirm and arrange payment.</p>
  </div>
  <div class="nf-fields">
    <div class="nf-field">
      <label class="nf-label" for="nf-order-form-name">Name<span class="nf-req" aria-hidden="true"> *</span></label>
      <input class="nf-input" id="nf-order-form-name" name="name" type="text" autocomplete="name" placeholder="Jane Smith" required>
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-order-form-email">Email<span class="nf-req" aria-hidden="true"> *</span></label>
      <input class="nf-input" id="nf-order-form-email" name="email" type="email" autocomplete="email" placeholder="jane@example.com" required>
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-order-form-phone">Phone</label>
      <input class="nf-input" id="nf-order-form-phone" name="phone" type="tel" autocomplete="tel" placeholder="+1 555 0100">
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-order-form-product">Product<span class="nf-req" aria-hidden="true"> *</span></label>
      <select class="nf-input" id="nf-order-form-product" name="product" required>
        <option value="">Choose a product</option>
        <option value="Sourdough loaf">Sourdough loaf</option>
        <option value="Box of 6 pastries">Box of 6 pastries</option>
        <option value="Celebration cake">Celebration cake</option>
      </select>
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-order-form-quantity">Quantity<span class="nf-req" aria-hidden="true"> *</span></label>
      <input class="nf-input" id="nf-order-form-quantity" name="quantity" type="number" inputmode="decimal" min="1" max="50" placeholder="1" required>
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-order-form-address">Delivery address<span class="nf-req" aria-hidden="true"> *</span></label>
      <textarea class="nf-input" id="nf-order-form-address" name="address" rows="4" placeholder="Street, city and postcode" required></textarea>
    </div>
    <div class="nf-field">
      <label class="nf-label" for="nf-order-form-notes">Notes</label>
      <textarea class="nf-input" id="nf-order-form-notes" name="notes" rows="4" placeholder="Allergies, delivery times or a message for the card"></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">Place order</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!</p>
    <p class="nf-success-text">We got your order and will email you to confirm.</p>
  </div>
</form>
<script>
(function () {
  var form = document.getElementById("nf-order-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" 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>
    Product
    <select name="product" required>
      <option value="" disabled selected>Choose one</option>
      <option value="Sourdough loaf">Sourdough loaf</option>
      <option value="Box of 6 pastries">Box of 6 pastries</option>
      <option value="Celebration cake">Celebration cake</option>
    </select>
  </label>
  <label>
    Quantity
    <input name="quantity" type="number" min="1" max="50" placeholder="1" required>
  </label>
  <label>
    Delivery address
    <textarea name="address" rows="5" placeholder="Street, city and postcode" required></textarea>
  </label>
  <label>
    Notes
    <textarea name="notes" rows="5" placeholder="Allergies, delivery times or a message for the card"></textarea>
  </label>
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Place order</button>
</form>
```

## Framework components

### React

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

export function OrderForm() {
  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! We got your order and will email you to confirm.</p>

  return (
    <form onSubmit={onSubmit}>
      <label>
        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>
        Product
        <select name="product" defaultValue="" required>
          <option value="" disabled>Choose one</option>
          <option value="Sourdough loaf">Sourdough loaf</option>
          <option value="Box of 6 pastries">Box of 6 pastries</option>
          <option value="Celebration cake">Celebration cake</option>
        </select>
      </label>
      <label>
        Quantity
        <input name="quantity" type="number" min="1" max="50" placeholder="1" required />
      </label>
      <label>
        Delivery address
        <textarea name="address" rows={5} placeholder="Street, city and postcode" required></textarea>
      </label>
      <label>
        Notes
        <textarea name="notes" rows={5} placeholder="Allergies, delivery times or a message for the card"></textarea>
      </label>
      <input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" style={{ display: 'none' }} />
      <button type="submit" disabled={status === 'sending'}>Place order</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 OrderForm() {
  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! We got your order and will email you to confirm.</p>

  return (
    <form onSubmit={onSubmit}>
      <label>
        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>
        Product
        <select name="product" defaultValue="" required>
          <option value="" disabled>Choose one</option>
          <option value="Sourdough loaf">Sourdough loaf</option>
          <option value="Box of 6 pastries">Box of 6 pastries</option>
          <option value="Celebration cake">Celebration cake</option>
        </select>
      </label>
      <label>
        Quantity
        <input name="quantity" type="number" min="1" max="50" placeholder="1" required />
      </label>
      <label>
        Delivery address
        <textarea name="address" rows={5} placeholder="Street, city and postcode" required></textarea>
      </label>
      <label>
        Notes
        <textarea name="notes" rows={5} placeholder="Allergies, delivery times or a message for the card"></textarea>
      </label>
      <input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" style={{ display: 'none' }} />
      <button type="submit" disabled={status === 'sending'}>Place order</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! We got your order and will email you to confirm.</p>
  <form v-else @submit.prevent="onSubmit">
    <label>
      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>
      Product
      <select name="product" required>
        <option value="" disabled selected>Choose one</option>
        <option value="Sourdough loaf">Sourdough loaf</option>
        <option value="Box of 6 pastries">Box of 6 pastries</option>
        <option value="Celebration cake">Celebration cake</option>
      </select>
    </label>
    <label>
      Quantity
      <input name="quantity" type="number" min="1" max="50" placeholder="1" required>
    </label>
    <label>
      Delivery address
      <textarea name="address" rows="5" placeholder="Street, city and postcode" required></textarea>
    </label>
    <label>
      Notes
      <textarea name="notes" rows="5" placeholder="Allergies, delivery times or a message for the card"></textarea>
    </label>
    <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
    <button type="submit" :disabled="status === 'sending'">Place order</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! We got your order and will email you to confirm.</p>
{:else}
  <form {onsubmit}>
    <label>
      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>
      Product
      <select name="product" required>
        <option value="" disabled selected>Choose one</option>
        <option value="Sourdough loaf">Sourdough loaf</option>
        <option value="Box of 6 pastries">Box of 6 pastries</option>
        <option value="Celebration cake">Celebration cake</option>
      </select>
    </label>
    <label>
      Quantity
      <input name="quantity" type="number" min="1" max="50" placeholder="1" required>
    </label>
    <label>
      Delivery address
      <textarea name="address" rows="5" placeholder="Street, city and postcode" required></textarea>
    </label>
    <label>
      Notes
      <textarea name="notes" rows="5" placeholder="Allergies, delivery times or a message for the card"></textarea>
    </label>
    <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
    <button type="submit" disabled={status === 'sending'}>Place order</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" 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>
    Product
    <select name="product" required>
      <option value="" disabled selected>Choose one</option>
      <option value="Sourdough loaf">Sourdough loaf</option>
      <option value="Box of 6 pastries">Box of 6 pastries</option>
      <option value="Celebration cake">Celebration cake</option>
    </select>
  </label>
  <label>
    Quantity
    <input name="quantity" type="number" min="1" max="50" placeholder="1" required>
  </label>
  <label>
    Delivery address
    <textarea name="address" rows="5" placeholder="Street, city and postcode" required></textarea>
  </label>
  <label>
    Notes
    <textarea name="notes" rows="5" placeholder="Allergies, delivery times or a message for the card"></textarea>
  </label>
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Place order</button>
</form>
```

## When to use it

- Bakeries, caterers and local food businesses
- Pre-orders for a product that is not out yet
- Makers selling a few products from a portfolio site
- Internal orders for merchandise or office supplies

## Customize it

### List your own products

Change the `<option>` elements to your products. Put the price in the option text, like "Sourdough loaf ($8)", so it is saved with the order.

### Order several products

Copy the product and quantity fields and give them new names, like `product2` and `quantity2`. Every named field is saved.

### Send a payment link

On Pro, redirect customers to a payment link after they order, or reply with an invoice.

## Frequently asked questions

### Can this order form take payments?

No. Nisuform collects the order and you arrange payment, for example with an invoice or a payment link. Never ask for card numbers in a form. Use a payment provider for that.

### How do I get notified of new orders?

Every order is emailed to you as soon as it arrives. On Pro, orders can also go to Slack, Discord, Telegram or a Google Sheet.

### Can customers order more than one product?

Yes. Duplicate the product and quantity fields with new names, or use checkboxes that share a name, which are saved as one comma-separated answer.
