cross-fetch
Universal WHATWG Fetch API ponyfill for Node, browsers, and React Native
Repository Health
Technical Analysis
cross-fetch provides a single fetch implementation that behaves consistently across Node.js, browsers, and React Native, so libraries and applications can call the WHATWG Fetch API without branching on runtime environment. It resolves to node-fetch under Node, the browser’s native fetch in browsers, and a React Native-compatible implementation on that platform, all behind one import.
The package ships both as a ponyfill (cross-fetch, the default import — does not touch global state) and as a polyfill (cross-fetch/polyfill, which assigns fetch/Headers/Request/Response onto globalThis for code that expects fetch to already exist globally). It also normalizes schemaless URLs (//example.com/path) to https:// on the server for parity with browser behavior.
What You Get
- A single
fetchimport that resolves to the correct implementation per environment (Node vianode-fetch, browser native, React Native-specific build) via package.json’sbrowser/react-native/mainfields - A ponyfill entry point (
cross-fetch) that does not mutate global scope, for libraries that want an explicit import - A polyfill entry point (
cross-fetch/polyfill) that assignsfetch/Headers/Request/Responseonto the global object for code expecting ambientfetch - Schemaless URL normalization (
//host/path->https://host/path) on the Node side for browser-like behavior parity - Bundled TypeScript type definitions (
index.d.ts)
Common Use Cases
- Isomorphic/universal JavaScript libraries and SDKs that must run identically in Node and the browser without environment-specific fetch code
- Server-side rendering (Next.js, Remix, etc.) codebases that share data-fetching logic between server and client bundles
- Test suites and Node scripts that need a spec-compliant
fetchbefore Node’s own native fetch was universally available - React Native apps that want the same fetch-based data layer code as their web counterpart
Under The Hood
Architecture - The package’s package.json routes to different entry files per consumer via the main (dist/node-ponyfill.js), browser (dist/browser-ponyfill.js), and react-native (dist/react-native-ponyfill.js) fields, letting bundlers and Node’s own resolution algorithm pick the right implementation automatically. The Node ponyfill (src/node-ponyfill.js) is a thin wrapper around node-fetch that additionally rewrites schemeless URLs to https:// before delegating, and re-exports Headers/Request/Response from the same underlying package. The separate polyfill/ subpackage and cross-fetch/polyfill entry point (src/node-polyfill.js) layer global assignment on top of the ponyfill only when global.fetch isn’t already defined, keeping the two usage modes cleanly separated.
Tech Stack - Plain JavaScript (CommonJS) with a single runtime dependency on node-fetch for the Node path; built with Rollup and esbuild plugins into per-target bundles, tested with Mocha/Chai (plus mocha-headless-chrome for real-browser test runs) and nock for HTTP mocking, with standard for linting.
Code Quality - Over 40 files under test/, organized by concern (test/fetch-api, test/module-system, test/setup), including a headless-Chrome run to validate actual browser behavior rather than only a jsdom approximation — a meaningfully more rigorous approach for a cross-environment compatibility shim. SECURITY.md documents a responsible-disclosure process, and Husky/commitlint enforce conventional commits, though the project’s own commit velocity has slowed since Node adopted native fetch.
API Design - The package deliberately mirrors the standard fetch(url, options) signature and Web-standard Headers/Request/Response classes exactly, so switching between cross-fetch and a native fetch requires no code changes — the entire value proposition is being invisible at the call site while resolving correctly per platform.