When JSON Isn't Enough

Sometimes you need to send an entire HTML form — or even an image or file upload — not just JSON. For this, use FormData.

// Read all fields from an existing form automatically
const form = document.querySelector('#upload-form');
const formData = new FormData(form);

// Add extra fields programmatically
formData.append('uploadedAt', new Date().toISOString());
formData.append('category', 'profile-photo');

const response = await fetch('/api/upload', {
  method: 'POST',
  body: formData // DO NOT set Content-Type manually!
});

const result = await response.json();
console.log('File uploaded:', result.url);

File Upload from an Input

const fileInput = document.querySelector('#avatar-input');
fileInput.addEventListener('change', async () => {
  const file = fileInput.files[0];
  const formData = new FormData();
  formData.append('avatar', file);
  formData.append('userId', '42');
  
  await fetch('/api/upload-avatar', { method: 'POST', body: formData });
});

Key Rules

  1. DO NOT manually set Content-Type: multipart/form-data. The browser must auto-generate it with the correct boundary separator — if you set it manually, the server cannot parse the body.
  2. FormData is the ONLY standard way to send File and Blob objects to a server.
Caution

If you manually set the Content-Type header for FormData in fetch, the request will almost certainly break on the server. Trust the browser!