cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

FormData in JavaScript

Collect named form fields, repeated values, and files with FormData, then send them safely with fetch or convert them on purpose.

By the end, you can
  • 01
    Pack a formPredict which controls new FormData(form) includes and why.
  • 02
    Edit the packageUse append, set, delete, has, get, getAll, and iteration without losing repeated names.
  • 03
    Send or convert intentionallySend multipart bodies, include files, and avoid Object.fromEntries surprises.

FormData packs a form

A form often has more than one kind of value: text, email addresses, checkboxes, radio buttons, multi-select choices, submit buttons, and files. Reading each control by hand is possible, but it is easy to forget a rule. FormData lets the browser apply the same rules it uses for a real form submission.

The main move is new FormData(form). It creates an ordered collection of name → value entries. If you pass the clicked submit button as the second argument, newer browsers can include that button’s name=value too.

Real-life analogyFormData is a shipping box packed from your form

Picture a shipping clerk walking across your form. Every enabled control with a label-like name gets packed. A checkbox group can put several items under the same label, and you can still add or replace items before shipping.

In real life: A labeled item in the box
In JavaScript: a name → value entry
In real life: The packing table
In JavaScript: the form element
In real life: Add another item
In JavaScript: append(name, value)
In real life: Replace all items with that label
In JavaScript: set(name, value)

Where the analogy stops: Real boxes do not have iteration order or getAll; FormData is still a JavaScript object with precise methods.

Definition

FormData is an iterable browser object containing ordered form entries whose values are strings or File/Blob objects.

What gets included?

INTERACTIVE

The browser does not pack every element inside a form. It collects successful controls: named controls that are not disabled, plus control-specific details such as checked checkboxes and selected options.

Pack the box: what does FormData include?
Submit handler sketchPop out in the code editor (opens in a new tab)JavaScript
form.addEventListener("submit", (event) => {  event.preventDefault();  const form = event.currentTarget;  const submitter = event.submitter;   // Newer browsers can include the clicked submit button.  const data = new FormData(form, submitter);  for (const [name, value] of data) {    console.log(name, value instanceof File ? value.name : value);  }});
Topics
Contact
Packed entriesSubmit the form to pack the box.
Submit the form to see the named, enabled, successful controls.
Try it yourself

Submit the real form, then compare the entries with the visible controls. Files stay on your machine; the playground only shows their name, size, and type.

Try selecting a file, unchecking boxes, changing the radio button, enabling the disabled field, and pressing different submit buttons.

Try these predictions: a readonly input is included, a disabled input is skipped, an unchecked checkbox is skipped, a nameless input is skipped, checked checkboxes with the same name create repeated entries, and an empty text field is included as "". An enabled named file input with no selection appears in current browsers as an empty File entry.

Included in new FormData(form)?
  • Disabled input with name
  • Readonly text input with name
  • Unchecked checkbox
  • Input without name
  • Two checked checkboxes named topics
  • Clicked submit button with name=value
  • <select multiple> with two selected options
  • Empty text input with name
Try it yourself
0 of 8 correct

Sort each card by the browser’s successful-control rules.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

append, set, get, getAll

STEP THROUGH

FormData is not a plain object. Repeated names are allowed and common: checkbox groups use them, <select multiple> uses them, and your own code can create them with append.

Step through append, set, get, and getAll
Step 0 of 7Ready
Your turn: follow the blue line

Step through repeated names, then switch line 4 between append and set.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
fd.append("topic", "forms");fd.append("topic", "files");fd.append("topic", "uploads");console.log(fd.get("topic"));console.log(fd.getAll("topic").join(", "));console.log([...fd.entries()].map(([name, value]) => name + "=" + value).join(" | "));
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Change line 4
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
FormData methods that are easy to mix up
MethodWhat it doesBest for
append(name, value)Adds another value after existing entries with that name.Checkboxes, multi-value keys, extra files.
set(name, value)Removes all old values for that name, then stores one new value.Single-value fields you want to overwrite.
get(name)Returns the first value, or null.Email, title, one selected radio value.
getAll(name)Returns an array of every value for that name.Checkbox groups and multi-selects.
has(name) / delete(name)Checks for at least one value / removes every value for that name.Optional values and cleanup before sending.

Iteration is also part of the toolset. You can use for (const [name, value] of fd), fd.entries(), fd.keys(), fd.values(), or [...fd]. The order is the insertion order you created or the form’s control order.

Sending files and requests

INTERACTIVE

A FormData object can be the body of a request. For a real upload you would write fetch("/profile", { method: "POST", body: formData }). Do not set the multipart Content-Type header yourself; the browser adds the boundary that matches the body.

Fake server: inspect a FormData request locally
Local request previewPop out in the code editor (opens in a new tab)JavaScript
async function previewRequest(formData) {  const request = new Request("/profile", {    method: "POST",    body: formData,  });   const parsed = await request.formData();  const multipart = await new Request("/profile", {    method: "POST",    body: formData,  }).text();   return {    parsed: [...parsed.entries()],    startsWithMultipart: multipart.includes("form-data"),  };}
Generated file upload partPop out in the code editor (opens in a new tab)JavaScript
const avatar = new Blob(["hello"], { type: "text/plain" });const fd = new FormData();fd.append("avatar", avatar, "avatar.txt");const file = fd.get("avatar");console.log(file.name);console.log(file.size);console.log(file.type);
Fake server outputstrings only
Run the local request preview.
Try it yourself

This creates a Request object in the browser and parses it with request.formData(). It does not fetch an external URL.

For a real fetch, pass the same FormData as body and let the browser set the multipart boundary.

Files are local until you send them. Selecting a file in the playground only lets the page show its name, size, and type. Generated data can be added too: formData.append("avatar", blob, "avatar.png").

URL-encoded bodies are for stringsPop out in the code editor (opens in a new tab)JavaScript
const fd = new FormData();fd.append("name", "Ada Lovelace");fd.append("topic", "forms");const body = new URLSearchParams(fd);console.log(body.toString());

new URLSearchParams(formData) is useful for old-style URL-encoded bodies when every value is a string. It is the wrong shape for real files.

Objects and repeated names

INTERACTIVE

You may see Object.fromEntries(formData) in examples. It is convenient for truly single-value forms, but it silently collapses repeated names. Objects are single-slot organizers: the last value for a property overwrites earlier ones.

Real-life analogyA list of labeled answers

FormData is a list of labeled answers. When you turn it into a plain object, each label has one line. If two answers use topics, the last one replaces the first.

In real life: Several answers use the same label
In JavaScript: Repeated FormData entries
In real life: One answer line per label
In JavaScript: One object property
In real life: The last answer stays visible
In JavaScript: The last value wins

Where the analogy stops: A form can have repeated answers. Plain Object.fromEntries does not make an array for them.

Object.fromEntries pitfall
Array-preserving helperPop out in the code editor (opens in a new tab)JavaScript
function formDataToObject(fd) {  const result = {};  for (const [name, value] of fd) {    if (Object.hasOwn(result, name)) {      result[name] = Array.isArray(result[name])        ? [...result[name], value]        : [result[name], value];    } else {      result[name] = value;    }  }  return result;} const fd = new FormData();fd.append("topic", "forms");fd.append("topic", "files");fd.append("age", 12);console.log(Object.fromEntries(fd).topic);console.log(formDataToObject(fd).topic.join(" + "));console.log(typeof formDataToObject(fd).age);
Converted objectfromEntries
  • topicfiles
  • age12
Try it yourself

Repeated names are safe inside FormData. They collapse only when you unpack them into a one-slot-per-name object.

Notice that age is still a string either way; convert numbers explicitly after validation.

Also notice type conversion: non-Blob values become strings. If a form field represents a number, validate and convert it on purpose after you read it.

Where you will use this

Use FormData for profile forms, contact forms with attachments, import tools, checkout notes, support tickets, and admin dashboards where a submit event already gives you a form element. It pairs naturally with the Form events lesson’s preventDefault() and the Form validation lesson’s checkValidity() / reportValidity().

A realistic submit handlerPop out in the code editor (opens in a new tab)JavaScript
form.addEventListener("submit", async (event) => {
  event.preventDefault();
  if (!form.reportValidity()) return;

  const data = new FormData(form, event.submitter);
  data.append("clientTimeZone", Intl.DateTimeFormat().resolvedOptions().timeZone);

  await fetch("/profile", {
    method: "POST",
    body: data,
  });
});

The next lesson, fetch & JSON, will focus on the request/response side. Here, the key is that the body is already prepared.

Common misconceptions

  • “FormData is JSON.” It is an iterable form body, usually sent as multipart data.
  • “Every input is included.” Disabled, nameless, unchecked checkbox/radio controls are skipped.
  • “Repeated names are wrong.” They are how checkbox groups and multi-selects work.
  • “Object.fromEntries is always safe.” It loses earlier repeated values.
  • “I should set Content-Type.” Let the browser add the multipart boundary.
Misconceptions corrected
MistakeBetter modelQuick check
FormData equals a plain objectIt is an ordered iterable with methods.Try [...fd] and fd.getAll(name).
Readonly means skippedReadonly is included; disabled is skipped.Use the pack-the-box playground.
Files are stringsFiles are File/Blob objects with name, size, and type.Inspect the file entry row.
Numbers stay numbersNon-Blob values are stringified.typeof fd.get("age") is string.

Practice

5 EXERCISES
Exercise 1 · Warm-upSort the packed controls

Predict the output. This checks whether set feels different from append.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const fd = new FormData();
fd.append("color", "red");
fd.append("color", "blue");
fd.set("color", "green");
console.log(fd.getAll("color").join(" & "));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upWhich object property survives?

    Predict what the object conversion prints.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const fd = new FormData();
    fd.append("tag", "a");
    fd.append("tag", "b");
    console.log(Object.fromEntries(fd).tag);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeBuild a URL-encoded string

      Predict the exact query string.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const fd = new FormData();
      fd.append("name", "Ada Lovelace");
      fd.append("topic", "forms");
      console.log(new URLSearchParams(fd).toString());

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFind the bug in this upload

        Explain the bug, then reveal the safer version.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        await fetch("/upload", {
          method: "POST",
          headers: { "Content-Type": "multipart/form-data" },
          body: formData,
        });
          Exercise 5 · ChallengeWrite an array-preserving converter

          Write the helper from the object section without looking back.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          function formDataToObject(fd) {
            const result = {};
            // Fill this in.
            return result;
          }

            Check your understanding

            7 QUESTIONS
            FormData quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What does new FormData(form) collect?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does fd.get print after two appends?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const fd = new FormData();
              fd.append("topic", "forms");
              fd.append("topic", "files");
              console.log(fd.get("topic"));

              Choose an answer to see the explanation.

            3. Question 3 of 7After fd.set("topic", "uploads"), what happens to earlier topic values?

              Choose an answer to see the explanation.

            4. Question 4 of 7Why should you not set the Content-Type header when sending FormData with fetch?

              Choose an answer to see the explanation.

            5. Question 5 of 7What does Object.fromEntries(fd).topic print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const fd = new FormData();
              fd.append("topic", "forms");
              fd.append("topic", "files");
              console.log(Object.fromEntries(fd).topic);

              Choose an answer to see the explanation.

            6. Question 6 of 7Which FormData values are possible?

              Choose an answer to see the explanation.

            7. Question 7 of 7What does typeof fd.get print for age?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const fd = new FormData();
              fd.append("age", 12);
              console.log(typeof fd.get("age"));

              Choose an answer to see the explanation.

            Key takeaways

            • new FormData(form) collects named, enabled successful controls in form order.
            • Repeated names are normal; use getAll or arrays when you need every value.
            • append adds; set replaces; delete removes all values for a name.
            • Values are strings or File/Blob objects. Convert numbers intentionally.
            • Send FormData as a fetch body without hard-setting multipart Content-Type.

            Final definition: FormData is an ordered, iterable package of form entries that follows browser submission rules and can be inspected, edited, converted, or sent.

            Up next: fetch & JSON.

            Want to revisit object conversion first? The published Object.keys, values & entries lesson pairs well with this one.

            CompleteFrontend Clear concepts. Working examples.