FormData in JavaScript: collecting and sending form fields

FormData reads every field of a form in one line and goes straight into a fetch call, files included. As long as each field carries a name.
3 min read
Believemy logo

A ten-field form, and the temptation to read every value one by one with a selector. The code works, then a field is renamed and nobody notices.

The browser already knows how to collect those values: that is what it does when a form is submitted without any JavaScript. A built-in object gives you access to that work.


Definition

FormData represents a set of name and value pairs, exactly the ones a form would send. Built from a form element, it reads every field at once. Built empty, it is filled by hand.

JAVASCRIPT
const form = document.querySelector("form");
const data = new FormData(form);

console.log(data.get("email"));       // "camille@example.com"
console.log(data.getAll("interest")); // ["design", "code"]

data.append("source", "newsletter");
console.log(Object.fromEntries(data));

A single name can carry several values, something no plain object can represent. get returns the first one, getAll returns them all, and Object.fromEntries keeps only the last.


The fields that are missing

The collection follows the rules of HTML, not those of your code. Four categories of field are left out without the slightest warning.

  • No name attribute: a field without one does not exist as far as the form is concerned.
  • Disabled: the disabled attribute removes the field from the submission.
  • Unchecked box: nothing is sent, there is no false value.
  • Submit button: its value only appears if you pass it as the second argument of the constructor.

That first line explains half of the empty submissions reported during development: the field is visible, filled in, and yet absent from the data.


Sending a form, files included

The object goes straight into the body of a fetch() call. It is the only simple way to transmit a file picked by a visitor.

JAVASCRIPT
form.addEventListener("submit", async (event) => {
  event.preventDefault();

  const response = await fetch("/api/signups", {
    method: "POST",
    body: new FormData(form),
  });

  console.log(response.ok);
});

The call to preventDefault() stops the classic page reload, without which the browser would submit the form itself.

Good to know

Never set the Content-Type header by hand for this kind of upload. The browser has to add a computed boundary that separates the fields; written manually, it is wrong and the server stops reading anything.


Frequently asked questions

Question

How do I send the same data as JSON?

Convert the set into an object first, then serialize it: JSON.stringify(Object.fromEntries(data)). Watch out for multi-value fields, which collapse into a single value, and for files, which do not survive the conversion. For a URL query string, new URLSearchParams(data) does the job directly.


Question

Can I read the file content before uploading?

Yes, the value attached to a file field is a File object, with its name, size and type. Its text() and arrayBuffer() methods return a promise on the content, which allows a preview or a size check before any network call.


Question

Why does my field appear twice?

Because append adds a value without replacing the ones already there, unlike set, which overwrites everything under that name. A submission retried after a failure, on a reused object, therefore duplicates the values. Rebuild the object on each attempt rather than patching it.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.