HTML5 FormData Polyfill for Browsers and Node.js

4.0.10 · maintenance · verified Tue Apr 21

The `formdata-polyfill` package provides a robust, spec-compliant `FormData` implementation for both browser environments and Node.js. For browsers, it acts as a polyfill, conditionally replacing the native `FormData` implementation to ensure consistent behavior across older browsers, including patches for `XMLHttpRequest.prototype.send`, `fetch`, and `navigator.sendBeacon`. For Node.js, it offers a pure, modular, and spec-compatible `FormData` class, verified by Web Platform Tests (WPT), which integrates seamlessly with `node-fetch` and modern `fetch` implementations. The current stable version is 4.0.10, released in late 2021. While new feature development appears to have slowed since then, it remains a maintained solution for consistent `FormData` handling, particularly for environments where native support is lacking or inconsistent. A key differentiator is its `formDataToBlob` utility, adopted by projects like Deno and Undici, enabling efficient serialization of `FormData` into `Blob` objects for HTTP requests.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates creating `FormData` instances in Node.js, appending various data types including files and stream-like objects, and sending them via `fetch`. It also shows how to convert `FormData` to a `Blob` using `formDataToBlob` and send the resulting `Blob`.

import fetch from 'node-fetch';
import File from 'fetch-blob/file.js';
import { fileFromSync } from 'fetch-blob/from.js';
import { FormData, formDataToBlob } from 'formdata-polyfill/esm.min.js';
import { Readable } from 'node:stream';
import fs from 'node:fs';

// Create a dummy README.md for the quickstart if it doesn't exist
if (!fs.existsSync('./README.md')) {
  fs.writeFileSync('./README.md', '# Quickstart Test\nThis is a test file for the formdata-polyfill quickstart.');
}

async function runQuickstart() {
  console.log('--- FormData with fetch ---');
  const filePath = './README.md';
  const file = fileFromSync(filePath);
  const fd = new FormData();

  fd.append('file-upload-simple', new File(['abc'], 'hello-world.txt', { type: 'text/plain' }));
  fd.append('file-upload-readme', file, 'quickstart-readme.md');

  // It's also possible to append file/blob look-a-like items
  // if you have streams coming from other destinations
  const dummyStream = Readable.from(['part1', 'part2', 'part3']);
  fd.append('file-upload-stream', {
    size: 123,
    type: 'video/mp4',
    name: 'cat-video.mp4',
    stream() { return dummyStream; },
    [Symbol.toStringTag]: 'File'
  }, 'cat-video.mp4');

  try {
    const response = await fetch('https://httpbin.org/post', { method: 'POST', body: fd });
    const data = await response.json();
    console.log('FormData fetch response:', data.files || data.form || data.data);
  } catch (error) {
    console.error('Error during FormData fetch:', error.message);
  }

  console.log('\n--- FormData converted to Blob with fetch ---');
  const anotherFd = new FormData();
  anotherFd.append('text', 'Hello Blob!');
  anotherFd.append('image', new File(['image data'], 'my-image.png', { type: 'image/png' }));

  const blob = formDataToBlob(anotherFd);
  console.log('Generated Blob:', { size: blob.size, type: blob.type });

  try {
    const response = await fetch('https://httpbin.org/post', {
      method: 'POST',
      body: blob,
      headers: {
        'Content-Type': blob.type,
        'Content-Length': blob.size
      }
    });
    const data = await response.json();
    console.log('Blob fetch response (parts of form data might be in `data` or `json` fields for httpbin):', data.data || data.json || data);
  } catch (error) {
    console.error('Error during Blob fetch:', error.message);
  }
}

runQuickstart();

view raw JSON →