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.
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:
const form = document.querySelector("form");
const formData = new FormData(form);
console.log(formData.get("email")); // Value of <input name="email">Pass a <form> 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:
// 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
| Method | What 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 <select multiple>:
<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:
console.log(formData.get("interests")); // "javascript" (first only)
console.log(formData.getAll("interests")); // ["javascript", "python"]Modifying Values
| Method | What 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:
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:
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:
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():
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:
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 <input type="file"> seamlessly: append the selected File object just like any other value:
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:
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:
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:
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:
const formData = new FormData(form);
formData.delete("csrf_token"); // Remove a field the server does not needWhen to Use FormData
| Use FormData when... | Use manual collection when... |
|---|---|
| The form has many fields | You only need 2-3 specific fields |
You are sending multipart/form-data | You are sending JSON |
| You have file uploads | No files involved |
| You need to iterate all fields uniformly | Each field needs unique processing |
| You want the simplest code | You 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
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.
Frequently Asked Questions
Can I use FormData without a form element?
How do I send FormData as JSON instead of multipart/form-data?
Does FormData include unchecked checkboxes?
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.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.