Appearance
Vanilla JS
Every example on this page uses plain DOM APIs and fetch - no library, no build step. Jump to just the one you need from the outline on the right, or read top to bottom for the full picture.
Basic submission
The plain HTML case - works with JavaScript disabled entirely:
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>AJAX submission
Intercept the submit, post the same data as JSON, and update the page in place instead of navigating away:
html
<form id="contact-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>
<p id="status" role="status"></p>
<script>
const form = document.getElementById('contact-form')
const status = document.getElementById('status')
form.addEventListener('submit', async (event) => {
event.preventDefault()
status.textContent = 'Sending…'
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()
status.textContent = 'Thanks - your message was sent.'
} else {
status.textContent = result.error
}
})
</script>File upload
FormData handles files the same way as text fields - just append the file, don't set Content-Type yourself (the browser sets the multipart boundary):
html
<form id="upload-form" action="https://f.bootform.com/{form_id}" method="POST">
<input name="email" type="email" required />
<input name="resume" type="file" required />
<button type="submit">Send</button>
</form>
<script>
document.getElementById('upload-form').addEventListener('submit', async (event) => {
event.preventDefault()
const form = event.target
const response = await fetch(form.action, {
method: 'POST',
headers: { Accept: 'application/json' },
body: new FormData(form), // includes the selected file automatically
})
const result = await response.json()
console.log(result.ok ? 'Uploaded' : result.error)
})
</script>A file upload requires the form to be claimed first - 422 FORM_NOT_CLAIMED otherwise. See Building your form.
Moderated content feed
Fetch a form's approved submissions and let visitors react, with no dashboard/API key involved - this endpoint is public and unauthenticated by design:
html
<ul id="feed"></ul>
<script>
async function loadFeed() {
const response = await fetch('https://f.bootform.com/forms/{form_id}/feed?per_page=20')
const { items } = await response.json()
const list = document.getElementById('feed')
list.innerHTML = items
.map(
(item) => `
<li data-id="${item.id}">
<p>${item.fields.message}</p>
<button onclick="react('${item.id}', 'up')">👍 ${item.reactions.up}</button>
<button onclick="react('${item.id}', 'down')">👎 ${item.reactions.down}</button>
</li>
`,
)
.join('')
}
async function react(submissionId, type) {
await fetch(`https://f.bootform.com/forms/{form_id}/feed/${submissionId}/react`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type }),
})
loadFeed() // refetch to show the updated count
}
loadFeed()
</script>See Moderated content feed for the report endpoint, pagination, and the hosted-widget alternative if you'd rather not write this yourself.