Using FormData API in JavaScript: Complete Guide

Learn the FormData API to read, modify, and send form data in JavaScript. Cover append, get, set, delete, entries, file uploads, and converting to JSON.

5 min read

FormData is a built-in API that represents form fields and their values as a set of key-value pairs. You can read values from an existing HTML form, modify them, add new ones, and send them to a server, all without manually selecting each input.

Here is the simplest usage: collect every field from a form in one line:

javascriptjavascript
const form = document.querySelector("form");
const formData = new FormData(form);
 
console.log(formData.get("email")); // Value of <input name="email">

Pass a &lt;form&gt; element to the FormData constructor, and it automatically reads every form control that has a name attribute: inputs, textareas, selects, and file inputs. Unchecked checkboxes and unselected radios are excluded.

Creating a FormData Object

There are two ways to create one:

javascriptjavascript
// From an existing form:
const formData = new FormData(document.querySelector("form"));
 
// From scratch (empty):
const manualData = new FormData();
manualData.append("username", "alice");
manualData.append("role", "admin");

The empty constructor is useful when you are building a data payload programmatically without any HTML form on the page, like for an API call that is not tied to a visible form.

Reading Values

MethodWhat it does
formData.get(name)Returns the first value for a key, or null
formData.getAll(name)Returns an array of all values for a key
formData.has(name)Returns true if the key exists
formData.entries()Returns an iterator of [key, value] pairs
formData.keys()Returns an iterator of all keys
formData.values()Returns an iterator of all values

get() vs getAll() matters for fields that can have multiple values, like checkboxes with the same name or &lt;select multiple&gt;:

htmlhtml
<input type="checkbox" name="interests" value="javascript" checked />
<input type="checkbox" name="interests" value="python" checked />

Both checkboxes share the name interests, so FormData collects both checked values under that one key. get() only returns the first, while getAll() returns every value:

javascriptjavascript
console.log(formData.get("interests"));      // "javascript" (first only)
console.log(formData.getAll("interests"));   // ["javascript", "python"]

Modifying Values

MethodWhat it does
formData.append(name, value)Adds a new value to a key (creates duplicates for multi-value)
formData.set(name, value)Replaces all existing values for a key with this one
formData.delete(name)Removes all values for a key

The difference between append and set is important when a key can have multiple values:

javascriptjavascript
const fd = new FormData();
 
fd.append("color", "red");
fd.append("color", "blue");
console.log(fd.getAll("color")); // ["red", "blue"]
 
fd.set("color", "green");
console.log(fd.getAll("color")); // ["green"] (replaced all)

Use append when you want to add another value to an existing key (like adding a second file). Use set when you want to overwrite whatever was there.

Iterating Over All Fields

FormData is iterable, so you can use for...of directly:

javascriptjavascript
for (const [name, value] of formData) {
  console.log(`${name}: ${value}`);
}

This is equivalent to calling formData.entries() directly in the for...of loop. If you prefer a callback style instead of iteration, forEach works the same way but passes the value before the name:

javascriptjavascript
formData.forEach((value, name) => {
  console.log(`${name}: ${value}`);
});

Converting FormData to JSON

FormData works directly as a fetch body for multipart/form-data, but sometimes an API expects JSON instead. Converting is a one-line call to Object.fromEntries():

javascriptjavascript
const formData = new FormData(form);
const jsonObject = Object.fromEntries(formData.entries());
const jsonString = JSON.stringify(jsonObject);
 
console.log(jsonObject);
// { email: "user@example.com", password: "secret123" }

Unlike a raw FormData body, a JSON body needs the Content-Type header set explicitly so the server knows how to parse it:

javascriptjavascript
await fetch("/api/submit", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(Object.fromEntries(formData)),
});

A word of caution: Object.fromEntries() only keeps the last value for duplicate keys. If your form has checkboxes with the same name or a multi-select, use getAll() and build the object manually instead.

Handling File Uploads

FormData is the standard way to handle file uploads. It works with &lt;input type="file"&gt; seamlessly: append the selected File object just like any other value:

javascriptjavascript
const fileInput = document.querySelector('input[type="file"]');
const formData = new FormData();
formData.append("avatar", fileInput.files[0]);

Uploading it works the same way as any other FormData request. The browser sets the Content-Type header to multipart/form-data with the correct boundary, so you do not need to set it manually:

javascriptjavascript
await fetch("/api/upload", {
  method: "POST",
  body: formData,
});

Sending FormData with fetch

FormData is designed to work as a fetch body without any conversion, which makes a full submit handler shorter than the JSON version shown earlier:

javascriptjavascript
form.addEventListener("submit", async (event) => {
  event.preventDefault();
  try {
    const response = await fetch("/api/submit", { method: "POST", body: new FormData(form) });
    if (response.ok) console.log("Form submitted successfully.");
  } catch (error) {
    console.log("Network error:", error.message);
  }
});

The full flow: prevent the default submission, collect all fields into FormData, send them with fetch, and handle the response. For more on form submission handling, see /javascript/javascript-form-handling-and-submission-tutorial.

Practical Pattern: Appending Extra Fields Before Submit

Sometimes you need to add fields that are not directly in the form, like a timestamp or a computed value:

javascriptjavascript
form.addEventListener("submit", async (event) => {
  event.preventDefault();
 
  const formData = new FormData(form);
  formData.append("submittedAt", new Date().toISOString());
  formData.append("source", "website");
 
  await fetch("/api/submit", { method: "POST", body: formData });
});

The reverse is just as easy: use delete() to strip a field the form included but the server should never receive, such as a client-only token:

javascriptjavascript
const formData = new FormData(form);
formData.delete("csrf_token"); // Remove a field the server does not need

When to Use FormData

Use FormData when...Use manual collection when...
The form has many fieldsYou only need 2-3 specific fields
You are sending multipart/form-dataYou are sending JSON
You have file uploadsNo files involved
You need to iterate all fields uniformlyEach field needs unique processing
You want the simplest codeYou need full control over the payload shape

For most form submissions, FormData is the right choice. It is fewer lines of code, handles edge cases (file uploads, encoding), and produces the correct request format automatically.

Common Mistakes

Using get() for multi-value fields. If a key has multiple values, get() returns only the first one. Use getAll() for checkboxes, multi-selects, or any field name that can appear more than once.

Assuming FormData includes unchecked checkboxes. Only checked checkboxes appear in FormData. formData.has("name") returns false for an unchecked checkbox too, since it was never included in the first place.

Setting Content-Type manually with FormData. When you pass FormData as a fetch body, the browser sets the multipart/form-data boundary automatically. Do not override it with a manual Content-Type header, or the server will not be able to parse the boundary.

Using Object.fromEntries on multi-value keys. Object.fromEntries keeps only the last value for duplicate keys. If your FormData has multiple values for the same name, build the object manually using getAll() instead. For validation patterns that work well with FormData, see /javascript/form-validation-with-javascript-complete-guide.

Rune AI

Rune AI

Key Insights

  • new FormData(formElement) collects all named fields from a form at once.
  • Use get(name) for single values, getAll(name) for multi-value fields like checkboxes.
  • append() adds a field; set() replaces it if it already exists.
  • FormData works directly as a fetch body with the correct content type.
  • Convert to JSON with Object.fromEntries(formData.entries()).
  • FormData handles file uploads natively, with no special handling needed.
RunePowered by Rune AI

Frequently Asked Questions

Can I use FormData without a form element?

Yes. Create an empty FormData object with new FormData() (no argument), then add fields manually with append() or set(). This is useful for building a data payload programmatically without any HTML form on the page.

How do I send FormData as JSON instead of multipart/form-data?

Convert the FormData to a plain object with Object.fromEntries(formData.entries()), then use JSON.stringify(). Send it with a Content-Type: application/json header in your fetch request.

Does FormData include unchecked checkboxes?

No. Only checked checkboxes and selected radio buttons are included. If you need to know whether a checkbox was present but unchecked, use formData.has('name') before submission.

Conclusion

FormData is the cleanest way to work with form values in JavaScript. It reads all named fields from a form in one line, supports manual field manipulation, works directly as a fetch body, and handles file uploads seamlessly. Whether you are reading, modifying, or sending form data, FormData is the right tool.