Plain HTML
Where the code goes
Paste the generated form wherever the form should appear in your page's HTML, for example inside <section id="contact">. The small script under it submits with fetch; without JavaScript the form posts normally.
The origin to allow
Add your site's hostname (for example example.com) as an allowed origin on the project. npx -y https://formto.io/dl/cli-latest.tgz init does this from package.json homepage.
Thank-you handling
With the script, the form shows “Thanks, we got it.” in place. Without it, formto shows a plain thank-you page, or redirects to your own page if you set a success redirect on the form.
File uploads
Add a field of type file to the definition and regenerate: the form switches to multipart/form-data and files are stored with the response. Your plan sets the size and count limits.
The code
Generated from the form’s fields (plain HTML with a small script). Replace YOUR_FORM_ID with your form’s public id, then restyle it; keep the field names and the hidden _gotcha honeypot.
<form id="formto-form" action="https://api.formto.io/f/YOUR_FORM_ID" method="post" enctype="application/x-www-form-urlencoded">
<h2>Contact</h2>
<p>
<label for="formto-name">Name</label>
<input type="text" id="formto-name" name="name" required>
</p>
<p>
<label for="formto-email">Email</label>
<input type="email" id="formto-email" name="email" autocomplete="email" required>
</p>
<p>
<label for="formto-message">Message</label>
<textarea id="formto-message" name="message" rows="5" required></textarea>
</p>
<!-- Spam trap: people never see or fill this field. -->
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" aria-hidden="true" style="position:absolute;left:-10000px">
<button type="submit">Send</button>
<p data-formto-status role="status" aria-live="polite"></p>
</form>
<script>
// Progressive enhancement: without JavaScript the form above posts natively.
;(function () {
var form = document.getElementById('formto-form')
if (!form || !window.fetch || !window.FormData) return
form.addEventListener('submit', function (event) {
event.preventDefault()
var status = form.querySelector('[data-formto-status]')
var button = form.querySelector('button[type=submit]')
button.disabled = true
fetch(form.action, { method: 'POST', body: new FormData(form), headers: { Accept: 'application/json' } })
.then(function (res) {
return res.json().then(function (body) {
if (!res.ok || !body.success) throw new Error(body.error || 'Something went wrong. Please try again.')
form.reset()
status.textContent = 'Thanks, we got it.'
})
})
.catch(function (err) {
status.textContent = err.message || 'Could not send. Please try again.'
})
.finally(function () {
button.disabled = false
})
})
})()
</script>
Other platforms: React, Next.js, Astro, WordPress, Squarespace, Webflow, Wix, Framer, Shopify.