Serialize an Entire Form in One Line with FormData
The amount of code I've seen (and written) that collects form values field by field - getElementById, .value, repeat times twenty - is embarrassing in hindsight. The browser reads whole forms in one constructor call.
form.addEventListener('submit', async (e) => {
e.preventDefault();
const data = new FormData(form); // every named field, including files
await fetch('/api/save', { method: 'POST', body: data });
});
Passing FormData as a fetch body sets the multipart Content-Type (with boundary) automatically. Do not set the header yourself - a hand-written multipart header without the boundary is the classic way to break uploads.
Want JSON instead? One more line
const json = Object.fromEntries(new FormData(form));
// { title: "Hello", category: "css", published: "1" }
Caveat that bites people: fromEntries keeps only the last value per key. Checkbox groups and multi-selects need getAll():
const data = new FormData(form);
const payload = {
...Object.fromEntries(data),
tags: data.getAll('tags[]'), // all checked values, not just the last
};
Things FormData knows that you'd get wrong
- Unchecked checkboxes are absent (matching classic form submission), checked ones send their value.
- Disabled fields are excluded; readonly fields are included. This mirrors real browser submits exactly.
- File inputs come through as File objects, ready to upload - no special handling.
- Fields outside the form tag but linked with the form attribute (
<input form="myform">) are picked up too. That attribute is its own little hidden gem for layouts where the submit button lives in a sticky footer.
Building FormData by hand
It's also the cleanest way to construct multipart payloads programmatically - I use this for pasted screenshot uploads:
const body = new FormData();
body.append('file', blob, 'screenshot.png');
body.append('folder', currentFolder);
The submitter detail almost nobody knows
Forms with multiple submit buttons (Save vs Save & Publish) traditionally lose the clicked button's value with FormData. The fix landed in all browsers: pass the submitter from the event.
const data = new FormData(form, e.submitter); // includes the clicked button's name/value
On the receiving end, PHP sees a FormData POST identically to a normal form submit: $_POST and $_FILES, no JSON decoding required. Sometimes the boring old formats are the smoothest path.
Full-stack web developer sharing practical tutorials and building tools that ship.
Got something on your mind?
My inbox is open - no forms disappearing into the void here.
- Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
- Hire me for a project Have something custom in mind? Let's talk scope and timelines.
- Product support Bought something here? I'll help you get it running.
I usually reply within 1-2 business days.