Glossary

FormData

FormData is a JavaScript interface that stores a list of form fields as name and value entries, where a value is a string or a File. It is defined in the WHATWG XMLHttpRequest Standard and is the usual way to upload files with fetch. When it is used as a request body, it is serialized as multipart/form-data, not as a query-style string.

How it works

You create an empty object with new FormData() or fill one from a form element with new FormData(formElement). The entry list keeps insertion order and allows duplicate names. The methods are:

  • append(name, value) adds an entry and never replaces one.
  • set(name, value) replaces all entries with that name by one, or appends if none exists.
  • get(name) returns the first value, or null if the name is missing.
  • getAll(name) returns every value for the name as an array, empty if none.
  • has(name) and delete(name) test for and remove all entries with that name.
  • It is iterable, so spreading it or calling keys(), values() and entries() works.

Strings are stored as strings. A Blob value is converted to a File, and append(name, blob, filename) lets you choose the file name. Without a filename, a plain Blob gets the file name "blob".

When you hand a FormData to fetch as body, the request uses the Content-Type multipart/form-data; boundary=..., where the boundary string is generated for you. Each entry becomes a part with its own Content-Disposition header, and file parts also carry their own Content-Type.

const fd = new FormData();
fd.append('tag', 'a'); fd.append('tag', 'b');
fd.set('name', 'Ada');
fd.append('doc', new Blob(['hello'], { type: 'text/plain' }), 'hi.txt');
console.log(fd.get('tag'), fd.getAll('tag'), fd.has('nope'), fd.get('nope'));
const f = fd.get('doc');
console.log(f instanceof File, f.name, f.size, f.type);
fd.set('tag', 'only');
console.log([...fd.keys()]);

Output from Node 22.22.0:

a [ 'a', 'b' ] false null
true hi.txt 5 text/plain
[ 'tag', 'name', 'doc' ]

On the wire, the same object becomes:

Content-Disposition: form-data; name="doc"; filename="hi.txt"
Content-Type: text/plain

hello

How do I send FormData with fetch?

Pass the object as the body and do not set a Content-Type header: fetch(url, { method: 'POST', body: fd }). The browser adds the multipart header with the boundary. To send JSON from the same data, use JSON.stringify(Object.fromEntries(fd)), though that keeps only the last value of any repeated name.

Common pitfalls

  • Setting Content-Type manually: writing multipart/form-data without a boundary makes servers fail with errors such as "no multipart boundary param in Content-Type". Leave the header out.
  • Using append when you meant set: repeated calls build duplicate entries, and servers may read only the first or last one.
  • Expecting a string back for files: get('doc') returns a File object, not text. Check typeof or instanceof File before using it as a string.
  • Object.fromEntries on repeated names: later values overwrite earlier ones, so a multi-select loses data. Use getAll.
  • Skipped form controls: disabled controls, unchecked checkboxes and unnamed fields are not included when you build from a form element.
  • JSON.stringify on FormData: it returns {} because entries are not own properties. Convert with Object.fromEntries(fd) or use [...fd] first.

Related terms

  • Fetch API — sends a FormData as a multipart request body.
  • Blob — a Blob appended to FormData becomes a File part.
  • File API — the File objects that file inputs produce and FormData carries.
  • Content-Type — the header that carries the multipart boundary.
  • Query string — the other form encoding, used for GET requests.
  • URL encoding — how application/x-www-form-urlencoded bodies are escaped.

See also

  • Cheatsheet: MIME Types Cheatsheet — media types such as multipart/form-data and application/x-www-form-urlencoded.