Skip to Content
Built InsFormData

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 fetch or 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

MethodWhat 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 fields

Last updated on