Join the AI Workshop and learn to build real-world apps with AI. A hands-on, practical program to level up your skills.
When uploading files using the Fetch API with FormData, you might encounter issues where the file data isn’t actually sent to the server.
The problem
Here’s a typical setup that might fail:
<form
encType='multipart/form-data'
action='/api/post'
method='POST'
onSubmit={async (e) => {
e.preventDefault()
const body = new FormData()
body.append('image', image)
body.append('title', title)
body.append('content', content)
const res = await fetch('/api/post', {
body,
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
},
})
}}
>
...
</form>
The file data isn’t being sent to the server correctly.
The solution
Do not set the Content-Type header manually.
Omit the Content-Type header from the fetch request (or omit the entire headers object):
const res = await fetch('/api/post', {
body,
method: 'POST',
// No headers!
})
Why this works
When you use FormData, the browser needs to:
- Set the
Content-Typetomultipart/form-data - Add a boundary parameter that separates the different parts of the form data
When you manually set Content-Type: 'multipart/form-data', you override the browser’s header without the boundary. The server then can’t parse the data correctly.
By omitting the header, the browser automatically sets:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary...
Correct example
const handleSubmit = async (e) => {
e.preventDefault()
const formData = new FormData()
formData.append('image', imageFile)
formData.append('title', title)
formData.append('content', content)
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
// Let the browser set the Content-Type header automatically
})
const result = await response.json()
}
Key takeaway
When using FormData with fetch, let the browser handle the Content-Type header. This applies to any situation where you’re sending files or multipart data.