Skip to content

Building your form

BootForm forms are schemaless: whatever name attributes you put on your inputs become the submission's fields, with no schema to declare up front. This page covers everything you can do with those fields, and the platform behavior around them.

html
<form action="https://f.bootform.com/{form_id}" method="POST">
  <input name="email" type="email" required />
  <textarea name="message"></textarea>
  <button type="submit">Send</button>
</form>

That's the whole minimum - see Getting started if you haven't posted your first submission yet. Everything below is what you can add on top of it.

Special fields

A handful of field names are reserved - BootForm strips them before storing the submission and uses them to control behavior instead:

FieldEffect
_redirect (or _next)Redirect the browser here after a successful submission
_replytoSets the notification email's Reply-To header
_subjectOverrides the notification email's subject line
_format=jsonForces a JSON response even for a classic (non-AJAX) browser POST
_languageLanguage for BootForm-generated content (confirmation pages, etc.)
_honeypotSpam trap - must stay empty; see Spam protection

Field values

BootForm doesn't control what value a field submits - the browser serializes your HTML before BootForm ever sees it, so standard HTML form semantics apply:

  • <select> submits the chosen <option>'s value attribute. If an <option> has no value attribute, its own text content is submitted instead (the HTML spec's fallback) - so <option value="us">United States</option> submits us, while a bare <option>United States</option> submits United States itself.
  • Radio buttons (<input type="radio">) submit whichever option's value attribute is checked. Unchecked radio buttons/checkboxes aren't submitted at all - the field is absent from the submission entirely, not present with an empty value.
  • Checkboxes with the same name (e.g. a "pick all that apply" group) submit one value per checked box, all sharing that name - the browser sends them as repeated key=value pairs, and BootForm joins every value for a repeated field name into one comma-separated string (tags=red&tags=blue becomes the field tags with value red,blue). BootForm forms have no array field type - this comma-joining is the only way a single field ends up holding more than one value.

File uploads

Send multipart/form-data and any file input's contents are stored as an attachment, downloadable later from the dashboard (fresh, short-lived links - see Managing submissions). Size limits scale with plan - see Pricing for the current per-file and total-storage caps per tier.

A plain HTML form handles this with no JavaScript at all - just add enctype="multipart/form-data" and a file input:

html
<form action="https://f.bootform.com/{form_id}" method="POST" enctype="multipart/form-data">
  <input name="email" type="email" required />
  <input name="resume" type="file" required />
  <button type="submit">Send</button>
</form>

Doing it via fetch instead - useful once you also want the JSON response below - reads that same form's fields straight out of FormData, so the HTML is identical; only the submit handling changes:

html
<form id="upload-form" action="https://f.bootform.com/{form_id}" method="POST" enctype="multipart/form-data">
  <input name="email" type="email" required />
  <input name="resume" type="file" required />
  <button type="submit">Send</button>
</form>
js
const form = document.getElementById('upload-form')

form.addEventListener('submit', async (event) => {
  event.preventDefault()

  const response = await fetch(form.action, {
    method: 'POST',
    headers: { Accept: 'application/json' },
    body: new FormData(form), // picks up the file input's contents automatically
  })
  const result = await response.json()
})

See this same pattern in React, Vue, Angular, or Vanilla JS.

One gotcha: a file upload posted to a form with no owning account yet (i.e. you haven't claimed it) is rejected outright (422 FORM_NOT_CLAIMED)

  • there's no plan/quota to validate a file against for a form nobody owns yet. Plain scalar-field submissions to an unclaimed form are unaffected; only file uploads need the form claimed first.

Spam protection

Honeypot, Cloudflare Turnstile CAPTCHA, an IP blocklist, and country restriction - all configured per form from the dashboard's Settings tab, and all covered in depth, with click-by-click Turnstile setup and exact response codes, on their own page: Spam protection.

Validation rules

Beyond spam filtering, you can require specific fields to be present and well-formed - configured per form from the dashboard's Settings tab. Each rule targets one field and can require it to be present, match a type (email, url, tel, number, or datetime), fall within a min/max (numbers and dates) or min/max length (text), match an accepted MIME type list (file fields), or

  • for email fields specifically - require a work email address rather than a free consumer provider.

A submission that fails validation is rejected with 422 and a structured errors array (one entry per failed field), or, for a classic non-AJAX browser POST, a redirect back to the form's page with ?bootform_error=...&bootform_field=... appended instead - there's no script running on that request to read a JSON body.

Responses and redirects

By default, a successful submission redirects the browser back to the page it came from. Two ways to change that:

  • Set _redirect (or _next) to send the browser somewhere specific after success.
  • Send Accept: application/json (or include _format=json) to get a 200 {"ok":true} JSON response instead of a redirect - the right choice for an AJAX/fetch() submission that updates the page in place rather than navigating away.
js
const form = document.querySelector('form')

form.addEventListener('submit', async (event) => {
  event.preventDefault()

  const response = await fetch(form.action, {
    method: 'POST',
    headers: { Accept: 'application/json' },
    body: new FormData(form),
  })
  const result = await response.json()

  if (result.ok) {
    form.reset()
    // show your own success message here - the page never navigated away
  } else {
    // result.error / result.code (and result.errors for per-field validation failures)
    console.error(result.error)
  }
})

If the form hasn't been claimed yet, a JSON response includes "status": "pending_claim" and a claim_url you can surface to whoever's testing the integration.

Autoresponder emails

On Pro plan and higher, BootForm can send a confirmation email back to whoever submitted the form

  • configured from the dashboard's Workflow tab with a subject and a Markdown body, both supporting {field_name} interpolation from the submission's own fields. This needs the submission to carry an email address (an email field, or _replyto) to have anywhere to send to; a submission with neither simply doesn't trigger a response, since BootForm forms have no fixed schema to check against ahead of time.