HTML5 FormData Polyfill for Browsers and Node.js
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
-
ReferenceError: FormData is not defined
cause In Node.js, attempting to use `FormData` without correctly importing the ESM module, or using CommonJS `require` directly on `formdata-polyfill/esm.min.js`.fixFor Node.js, ensure you are using ES Modules and import explicitly: `import { FormData } from 'formdata-polyfill/esm.min.js;'` -
TypeError: fd._blob is not a function
cause Trying to pass a `FormData` instance directly to the `body` of `new Request()` or `new Response()` in a browser environment where the polyfill is active, without manually converting it to a `Blob`.fixExplicitly convert the `FormData` to a `Blob` using `fd._blob()`: `new Request(url, { method: 'post', body: fd._blob ? fd._blob() : fd })`. -
SyntaxError: Unexpected token 'class' (or similar ESNext syntax error on module load)
cause Running the Node.js ESM module on an unsupported Node.js version, below `12.20.0`, which lacks support for modern JavaScript features used by the package.fixUpgrade your Node.js environment to version `12.20.0` or higher to meet the package's engine requirements.
Warnings
- breaking Prior to version 3.x, the browser polyfill did not globally replace the native `FormData` implementation, requiring manual instantiation or explicit patching. Upgrading to 3.x and above automatically replaces the global `FormData`, simplifying usage but potentially causing conflicts if other libraries expected a specific native `FormData` instance or attempted their own patching.
- gotcha When using the browser polyfill, direct passing of a `FormData` instance to the `body` option of `new Request()` or `new Response()` constructors is not fully supported due to limitations in patching native constructors. Developers must explicitly call `fd._blob()` on the `FormData` instance to serialize it into a `Blob` before passing it to the constructor.
- gotcha The Node.js ESM version (`formdata-polyfill/esm.min.js`) has higher platform dependencies, specifically requiring Node.js version `12.20.0` or higher. This is due to its reliance on modern JavaScript features like classes, symbols, and private fields. Using it with older Node.js versions will result in syntax errors during module loading.
- gotcha The browser polyfill deeply patches global objects and prototypes such as `XMLHttpRequest.prototype.send`, the global `fetch` function, and `navigator.sendBeacon`. This extensive patching can lead to unforeseen conflicts or unexpected behavior if other libraries or polyfills attempt to modify the same native APIs, requiring careful testing in complex front-end environments.
Install
-
npm install formdata-polyfill -
yarn add formdata-polyfill -
pnpm add formdata-polyfill
Imports
- FormData
const FormData = require('formdata-polyfill');import { FormData } from 'formdata-polyfill/esm.min.js'; - formDataToBlob
import formDataToBlob from 'formdata-polyfill/esm.min.js';
import { formDataToBlob } from 'formdata-polyfill/esm.min.js'; - Polyfill activation (browser)
import { FormData } from 'formdata-polyfill';import 'formdata-polyfill';
Quickstart
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();