FormData
The built-in interface for reading form field values. Pass it a <form> element and it collects every field by its name attribute.
When to use it
- Reading form values — after a submit event, grab all fields at once
- File uploads — FormData handles
<input type="file">natively - Sending to APIs — pass directly to
fetchor convert to JSON - Dynamic fields — append/delete entries programmatically
Anatomy
const form = document.querySelector('form')
const fd = new FormData(form)
fd.get('email') // 'alice@test.com' — single value by name
fd.getAll('tags') // ['js', 'css'] — all values for a name (multi-select, checkboxes)
fd.has('email') // true — check if a field exists
fd.set('email', 'new@test.com') // replace a value
fd.append('tag', 'html') // add another value (doesn't replace)
fd.delete('email') // remove a field
// iterate
fd.entries() // iterator of [name, value] pairs
fd.keys() // iterator of field names
fd.values() // iterator of field values
// convert to plain object
Object.fromEntries(fd) // { email: 'alice@test.com', name: '...' }At a glance
| Method | What it does |
|---|---|
.get(name) | Single value for a field |
.getAll(name) | All values for a field (checkboxes, multi-select) |
.has(name) | Check if a field exists |
.set(name, value) | Set/replace a value |
.append(name, value) | Add a value (keeps existing) |
.delete(name) | Remove a field |
.entries() | Iterator of [name, value] pairs |
Object.fromEntries(fd) | Convert to a plain object |
1. Simple — read form values
const form = document.getElementById('contact-form')
form.addEventListener('submit', (e) => {
e.preventDefault()
const fd = new FormData(form)
const name = fd.get('name') // value of <input name="name">
const email = fd.get('email') // value of <input name="email">
const message = fd.get('message')
})2. Intermediate — convert to JSON and send
form.addEventListener('submit', async (e) => {
e.preventDefault()
// fromEntries converts FormData → plain object → JSON
const data = Object.fromEntries(new FormData(form))
// { name: 'Alice', email: 'alice@test.com', message: 'Hello' }
await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
})3. Advanced — file upload with progress
const form = document.getElementById('upload-form')
form.addEventListener('submit', async (e) => {
e.preventDefault()
const fd = new FormData(form)
const file = fd.get('avatar') // File object from <input type="file">
file.name // 'photo.jpg'
file.size // 204800 (bytes)
file.type // 'image/jpeg'
// send as multipart/form-data — no Content-Type header needed
// the browser sets the correct boundary automatically
await fetch('/api/upload', {
method: 'POST',
body: fd, // pass FormData directly — not JSON.stringify
})
})When sending FormData directly to fetch, don’t set Content-Type — the browser adds multipart/form-data with the correct boundary.
Gotchas
Fields need a name attribute
<!-- this input WON'T appear in FormData — no name attribute -->
<input type="text" id="phone" />
<!-- this one WILL — name is what FormData reads -->
<input type="text" id="phone" name="phone" />fromEntries loses duplicate keys
// checkboxes with the same name produce multiple values
// <input type="checkbox" name="color" value="red" checked>
// <input type="checkbox" name="color" value="blue" checked>
const fd = new FormData(form)
fd.getAll('color') // ['red', 'blue'] — both values
Object.fromEntries(fd) // { color: 'blue' } — only the last one!
// use getAll for multi-value fieldsRelated
- Event —
e.preventDefault()stops the form from reloading - Object.fromEntries() — convert FormData to a plain object
Last updated on