Plain HTML
No framework, no build, no JS. Drop a form on any page and you're done.
The four lines
<form action="https://formspring.io/f/r2EdO-orF-3S" method="POST">
<input name="email" type="email" required />
<textarea name="message" required></textarea>
<button>Send</button>
</form>
That's it. The browser handles validation. Formspring accepts the POST, returns a 200 HTML page, you're done.
With a thank-you redirect
Add a hidden field. Formspring 303s the user to that URL after a successful submission:
<form action="https://formspring.io/f/r2EdO-orF-3S" method="POST">
<input type="hidden" name="_redirect" value="https://example.com/thanks" />
<input name="email" type="email" required />
<textarea name="message" required></textarea>
<button>Send</button>
</form>
The redirect target must be an absolute URL on a domain you've added to the form's allowlist (set in Form settings → Domains).
Honeypot
Add a hidden field bots will fill but humans won't. We drop submissions where it's not empty.
<form action="https://formspring.io/f/r2EdO-orF-3S" method="POST">
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off"
style="position:absolute;left:-9999px;" aria-hidden="true" />
<input name="email" type="email" required />
<textarea name="message" required></textarea>
<button>Send</button>
</form>
Don't use display: none — some bots ignore styles entirely or scrape the rendered DOM. Move the field offscreen instead.
Native validation
The browser handles required fields, email format, pattern matching, and min/max lengths.
<input name="email" type="email" required />
<input name="phone" type="tel" pattern="[0-9 +()-]{6,}" />
<input name="age" type="number" min="18" max="120" />
<textarea name="message" required minlength="10" maxlength="1000"></textarea>
The browser surfaces errors automatically; no JS. Add novalidate to the form if you want to skip browser validation and rely entirely on Formspring's server-side validation.
Multiple checkboxes / multi-select
Use the same name for related checkboxes. Formspring receives an array.
<fieldset>
<legend>Interested in</legend>
<label><input type="checkbox" name="interests[]" value="forms" /> Forms</label>
<label><input type="checkbox" name="interests[]" value="webhooks" /> Webhooks</label>
<label><input type="checkbox" name="interests[]" value="ai" /> AI features</label>
</fieldset>
The submission's payload.interests will be a JSON array.
File upload
<form action="https://formspring.io/f/r2EdO-orF-3S" method="POST" enctype="multipart/form-data">
<input name="email" type="email" required />
<input name="attachment" type="file" />
<button>Send</button>
</form>
enctype="multipart/form-data" is required for file uploads; without it the browser sends only filenames, not bytes. Files are stored, scanned, and exposed as signed URLs in webhooks.
What plain HTML can't do
- Inline error rendering (the user gets redirected on failure too unless you add JS).
- Optimistic UI / loading state.
- Any kind of multi-step flow.
For any of those, pick a framework recipe — Next.js, SvelteKit, Astro, or just vanilla React.