FormData
A spec-compliant FormData polyfill for browsers and Node.js, with built-in multipart Blob conversion.
Repository Health
Technical Analysis
formdata-polyfill fills the long-standing gap in the platform’s FormData implementation: full support for delete(), get(), getAll(), has(), set(), entries()/keys()/values(), and for…of iteration, in browsers and JS engines that only ever shipped a partial (append-only) implementation. Rather than patching the native object in place, it conditionally replaces the global FormData when the native one is missing the newer methods, and transparently monkey-patches XMLHttpRequest.send, fetch, and navigator.sendBeacon so existing code that calls fetch(url, { body: formData }) keeps working without changes.
For Node.js it ships a second, independent implementation: a from-scratch ESM class built on private class fields and Symbol.hasInstance brand-checking, with a single dependency (fetch-blob) supplying spec-compliant Blob/File objects. Both builds share the same underlying idea for wire serialization — formDataToBlob() walks a FormData’s entries and synchronously produces a multipart/form-data-encoded Blob without eagerly reading file contents into memory, a detail specific enough that both Deno and Undici vendored a version of this exact function into their own runtimes.
What You Get
- A full FormData class with append, delete, get, getAll, has, set, entries, keys, values and for…of iteration support
- Transparent monkey-patching of XMLHttpRequest.send, fetch, and navigator.sendBeacon so existing FormData-using code keeps working unmodified
- A dependency-free-in-the-browser build and a Node/ESM build backed only by fetch-blob for Blob/File support
- formDataToBlob(), a pure function that serializes any FormData into a spec-correct multipart/form-data Blob without buffering file contents upfront
Common Use Cases
- Polyfilling FormData in older browsers that lack delete/set/entries support
- Giving pre-v18 Node.js scripts a real, spec-compliant FormData for use with node-fetch
- Converting a FormData instance to a Blob so a low-level HTTP client (raw http.request, not fetch/XHR) can stream it as a multipart request body
- Uploading large files via FormData without eagerly reading their contents into memory
Under The Hood
Architecture The package ships two independent implementations in one repo: a legacy browser-patching module (FormData.js) that conditionally replaces the native FormData global and monkey-patches XMLHttpRequest.prototype.send, fetch, and navigator.sendBeacon so FormData instances are transparently serialized before being sent; and a modern ESM/Node module (esm.min.js) that defines a spec-compliant FormData class from scratch using private class fields and Symbol.hasInstance brand-checking, with no dependency on any browser API. A third piece of logic, formDataToBlob (present standalone in formdata-to-blob.js and inlined again inside esm.min.js), implements the multipart/form-data serialization as a pure function that walks a FormData’s entries and builds a boundary-delimited Blob, keeping wire-format logic decoupled from the FormData class itself. There’s no build step affecting semantics — build.js just runs the Google Closure Compiler to minify FormData.js into formdata.min.js. The design is deliberately simple and file-scoped, with the one piece of logic other runtimes actually depend on being the multipart boundary/escaping code, which Deno and Undici each vendored a version of.
Tech Stack Pure vanilla JavaScript with zero runtime dependencies for the browser polyfill and a single dependency, fetch-blob (^3.1.2), for the Node/ESM build, which supplies spec-compliant Blob/File implementations. The package dual-publishes CommonJS (formdata.min.js as main) and native ESM (esm.min.js, hand-minified rather than bundler output), targets Node >=12.20.0, and involves no framework, ORM, or database — it’s a low-level Web API polyfill. Dev tooling is limited to google-closure-compiler for minification plus @types/node and @types/google-closure-compiler for type-checking; there’s no bundler or transpiler in the loop.
Code Quality Testing is real but runtime-oriented rather than unit-style: test-esm.js runs assertions against the ESM implementation directly in Node, test-wpt-in-node.js replays the actual Web Platform Tests conformance suite for FormData against the polyfill via a custom HTTP loader, and test-polyfill.html offers a browser-based manual test page. Running WPT — the same suite browsers use to validate their own FormData implementation — against a small polyfill is a notably rigorous choice. Error handling is minimal but intentional: argument-count guards throw spec-accurate TypeErrors matching native FormData’s own error wording. There’s no TypeScript source (only a hand-written esm.min.d.ts declaration file), no visible root linter config, and no CI workflow beyond issue/PR templates, so conformance is enforced by WPT rather than static tooling.
API Design
The public surface deliberately mirrors the native FormData API one-for-one, so there’s zero new API to learn — usage is import 'formdata-polyfill' for the browser patch, or import { FormData } from 'formdata-polyfill/esm.min.js' for Node, and everything else behaves like the platform object. The one non-obvious extension, formDataToBlob(), is a single pure function with an optional BlobClass parameter, keeping the escape hatch for manual serialization (needed because Request/Response constructors can’t be monkey-patched) narrow and self-documenting rather than adding a parallel API surface.