The form Tag and FormData in Practice
In the previous article, we introduced the multipart/form-data request format and the problem it aims to solve. In this article, we will explain practical problems and use cases encountered in real-world development.
Specifically, this article covers the following topics related to form applications:
- What happens under the hood with the
<form/>tag - Using
FormDatain JavaScript - Combining
FormDatawithfetch - How JavaScript handles file uploads
Revisiting the form Tag
HTML’s form tag actually has many implementation details handled by the browser that developers sometimes overlook. Take the following example:
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="text" name="name" />
<input type="file" name="file" />
<button>
Submit
</button>
</form>
Without any JavaScript code, clicking the Submit button prompts the browser to handle these tasks for you:
- Serialize the
nameandfileinput fields - Send an HTTP request using the POST method with
Content-Type: multipart/form-data - Read the file and append it to the request payload (if the file exists)
Before single-page applications and front-end frameworks became popular, filling out a form, submitting it, and redirecting to another page was the standard approach. However, as the amount of input data grew or when only part of the page needed an update (such as posting a comment), having a full page reload every time provided a poor user experience. Consequently, the practice of calling APIs via AJAX and dynamically updating the UI with JavaScript gradually emerged.

Although dynamic updates indeed improved user experience, implementing good form design requires considering many details:
- Error handling
- State transitions
- Data persistence
- Accessibility
Often, if just one of these aspects is handled poorly, users might even prefer the simplicity of a classic full-page refresh form. For admin dashboards or back-office applications, using <form> elements with full-page refreshes can often save significant development time and may even run more reliably by leveraging the browser’s built-in mechanisms.
Using FormData in JavaScript
FormData defines an interface that makes it easy for developers to work with key/value pairs, most commonly in form handling. You can instantiate and use FormData like this:
const formData = new FormData()
// key value
formData.append('name', 'Kalan');
If you pass a form element into FormData, it will automatically serialize the input values directly into the FormData object:
<form id="form" enctype="multipart/form-data" action="/upload" method="POST">
<input type="text" name="name" />
<input type="file" name="file" />
<button>Submit</button>
</form>
<script>
const formData = new FormData(document.getElementById('form'));
formData.get('name'); // 取得目前 input 的值
formData.get('file'); // 取得目前的檔案
</script>

In addition, if you pass FormData into the body of fetch, the browser will automatically send it formatted as multipart/form-data:
const formData = new FormData();
formData.append('name', 'Kalan');
formData.append('file', new File(['Hello World'], 'file.txt', { type: 'text/plain' }))
fetch('/upload', {
method: 'POST',
body: formData
})
After executing the JavaScript code above and inspecting the Network tab, you will notice that even though Content-Type was not explicitly defined, the browser still sends it as multipart/form-data, and the serialization of the form data is also handled by the browser.

Summary
These two articles have covered the use cases and practical applications of multipart/form-data. The first article explained the meaning of Content-Disposition, the purpose of boundaries, and how a multipart/form-data request is constructed. This second article demonstrated how to practically use forms and FormData, as well as how to manipulate FormData and handle file uploads using JavaScript.
For the server, a file upload from a web page is essentially just an HTTP request. The server must parse the data based on the header information and the format defined by multipart/form-data to extract the file content correctly. Typically, this parsing is already handled by backend frameworks, but it is worth emphasizing that there is no magic behind uploading files on the web—it is fundamentally built upon HTTP requests.
Related Posts
- Recreating My Room with Three.js Using React Three Fiber, I brought my real room into the browser—turning physical objects into an interactive table of contents, and using spatial memory to tell the story of my life and work over the past few years.
- Things to Keep in Mind When Using Images in Frontend Development Expanding on Jake Archibald's article, this post organizes how modern responsive images should be written: why width/height are still necessary, when to use CSS aspect-ratio, how to choose between AVIF and WebP, and using picture/source/srcset for art direction on mobile devices.
- CSS field-sizing — Auto-resize Form Elements with a Single Line of CSS Previously, auto-resizing a textarea required listening to scrollHeight in JavaScript. With CSS field-sizing: content, a single line replaces it all, supporting textarea, input, and select. This article covers the pain points of older approaches and how to use field-sizing.
- Make Your Link Underlines Look Better: text-underline-offset By default, underlines sit very close to the text. Some designers dislike this look, and personally, I don't think it looks great either.