Lesson guide

Learning objectives

  • Explain the main purpose of FormData in JavaScript.
  • Identify the syntax, APIs, or concepts introduced in this lesson.
  • Use the examples to predict how JavaScript will behave before you run similar code.
  • Connect this topic to nearby lessons in Network requests.

Real-world context

This lesson matters when you need to recognize where FormData fits into real JavaScript programs. This chapter is about sending HTML forms: with or without files, with additional fields and so on.

Key ideas

  • FormData is part of the Network requests chapter, so it builds on the surrounding concepts rather than standing alone.
  • Read each code example in two passes: first for the result, then for the rule that explains the result.
  • When a section compares similar features, focus on the condition that makes you choose one feature over another.

Key terms

  • FormData
  • Network requests
  • JavaScript

This chapter is about sending HTML forms: with or without files, with additional fields and so on.

FormData objects can help with that. As you might have guessed, it’s the object to represent HTML form data.

The constructor is:

javascript
let formData = new FormData([form]);

If HTML form element is provided, it automatically captures its fields.

The special thing about FormData is that network methods, such as fetch, can accept a FormData object as a body. It’s encoded and sent out with Content-Type: multipart/form-data.

From the server point of view, that looks like a usual form submission.

Sending a simple form

Let’s send a simple form first.

As you can see, that’s almost one-liner:

markup
<form id="formElem">
  <input type="text" name="name" value="John">
  <input type="text" name="surname" value="Smith">
  <input type="submit">
</form>

<script>
  formElem.onsubmit = async (e) => {
    e.preventDefault();

    let response = await fetch('/article/formdata/post/user', {
      method: 'POST',
      body: new FormData(formElem)
    });

    let result = await response.json();

    alert(result.message);
  };
</script>

In this example, the server code is not presented, as it’s beyond our scope. The server accepts the POST request and replies “User saved”.

FormData Methods

We can modify fields in FormData with methods:

  • formData.append(name, value) – add a form field with the given name and value,
  • formData.append(name, blob, fileName) – add a field as if it were <input type="file">, the third argument fileName sets file name (not form field name), as it were a name of the file in user’s filesystem,
  • formData.delete(name) – remove the field with the given name,
  • formData.get(name) – get the value of the field with the given name,
  • formData.has(name) – if there exists a field with the given name, returns true, otherwise false

A form is technically allowed to have many fields with the same name, so multiple calls to append add more same-named fields.

There’s also method set, with the same syntax as append. The difference is that .set removes all fields with the given name, and then appends a new field. So it makes sure there’s only one field with such name, the rest is just like append:

  • formData.set(name, value),
  • formData.set(name, blob, fileName).

Also we can iterate over formData fields using for..of loop:

javascript
let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');

// List key/value pairs
for(let [name, value] of formData) {
  alert(`${name} = ${value}`); // key1 = value1, then key2 = value2
}

Sending a form with a file

The form is always sent as Content-Type: multipart/form-data, this encoding allows to send files. So, <input type="file"> fields are sent also, similar to a usual form submission.

Here’s an example with such form:

markup
<form id="formElem">
  <input type="text" name="firstName" value="John">
  Picture: <input type="file" name="picture" accept="image/*">
  <input type="submit">
</form>

<script>
  formElem.onsubmit = async (e) => {
    e.preventDefault();

    let response = await fetch('/article/formdata/post/user-avatar', {
      method: 'POST',
      body: new FormData(formElem)
    });

    let result = await response.json();

    alert(result.message);
  };
</script>

Picture:

Sending a form with Blob data

As we’ve seen in the chapter Fetch, it’s easy to send dynamically generated binary data e.g. an image, as Blob. We can supply it directly as fetch parameter body.

In practice though, it’s often convenient to send an image not separately, but as a part of the form, with additional fields, such as “name” and other metadata.

Also, servers are usually more suited to accept multipart-encoded forms, rather than raw binary data.

This example submits an image from <canvas>, along with some other fields, as a form, using FormData:

markup
<body style="margin:0">
  <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas>

  <input type="button" value="Submit" onclick="submit()">

  <script>
    canvasElem.onmousemove = function(e) {
      let ctx = canvasElem.getContext('2d');
      ctx.lineTo(e.clientX, e.clientY);
      ctx.stroke();
    };

    async function submit() {
      let imageBlob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));

      let formData = new FormData();
      formData.append("firstName", "John");
      formData.append("image", imageBlob, "image.png");

      let response = await fetch('/article/formdata/post/image-form', {
        method: 'POST',
        body: formData
      });
      let result = await response.json();
      alert(result.message);
    }

  </script>
</body>

Please note how the image Blob is added:

javascript
formData.append("image", imageBlob, "image.png");

That’s same as if there were <input type="file" name="image"> in the form, and the visitor submitted a file named "image.png" (3rd argument) with the data imageBlob (2nd argument) from their filesystem.

The server reads form data and the file, as if it were a regular form submission.

Common mistakes

  • Skipping the small examples and then missing the exact rule that FormData depends on.
  • Copying code without changing one value at a time to see which part controls the result.
  • Treating similar-looking syntax or APIs as interchangeable before checking their edge cases.

Summary

FormData objects are used to capture HTML form and submit it using fetch or another network method.

We can either create new FormData(form) from an HTML form, or create an object without a form at all, and then append fields with methods:

  • formData.append(name, value)
  • formData.append(name, blob, fileName)
  • formData.set(name, value)
  • formData.set(name, blob, fileName)

Let’s note two peculiarities here:

  1. The set method removes fields with the same name, append doesn’t. That’s the only difference between them.
  2. To send a file, 3-argument syntax is needed, the last argument is a file name, that normally is taken from user filesystem for <input type="file">.

Other methods are:

  • formData.delete(name)
  • formData.get(name)
  • formData.has(name)

That’s it!

Predict

Before running this FormData model, predict the seven output lines. It models constructing data from form fields, append, set, delete, duplicate field names, and adding Blob-like file data with a filename.

javascript
Reveal explanation

The output is roles:admin|editor, name:John:Brown, has image:true, has role:false, name=John, surname=Brown, and image=image.png:image/png:2048. new FormData(form) captures existing form controls. append() adds another value and allows repeated names, which is common for multiple fields. set() replaces all existing fields with that name. delete(), get(), and has() manage individual names. The three-argument append(name, blob, fileName) represents a file upload field, so servers receive it like a normal multipart file field.

Try it

Replace append("role", "editor") with set("role", "editor"), then predict the roles: line.

Practice

  • Rewrite one example from this lesson without looking at the original, then run it and compare the result.
  • Change one input, operator, method call, or option in a code sample and predict what will happen before running it.
  • Explain FormData in your own words as if you were reviewing it with another learner.

Keep learning

Continue with Fetch: Download progress when you are ready for the next lesson.