Generally speaking, the "exports"
field superseded the "module"
field. "module"
itself has never been an official standard but it became so widespread that at some point it was a de facto standard.
Note that the "module"
field is still being used by TypeScript if tsconfig's moduleResolution
is set to node
; see microsoft/TypeScript#50794 for more info.
Of course, there's nothing wrong in having both of these defined at the same time if your aim is to be as backwards compatible as possible.
Regarding the difference between "main"
and "exports"
, as per Node documentation:
In a package's package.json
file, two fields can define entry points for a package: "main"
and "exports"
. Both fields apply to both ES module and CommonJS module entry points.
The "main"
field is supported in all versions of Node.js, but its capabilities are limited: it only defines the main entry point of the package.
The "exports"
provides a modern alternative to "main"
allowing multiple entry points to be defined, conditional entry resolution support between environments, and preventing any other entry points besides those defined in "exports". This encapsulation allows module authors to clearly define the public interface for their package.
For new packages targeting the currently supported versions of Node.js, the "exports"
field is recommended. For packages supporting Node.js 10 and below, the "main"
field is required. If both "exports"
and "main"
are defined, the "exports"
field takes precedence over "main"
in supported versions of Node.js.
Keep in mind that the "main"
field may still be used by other online tools like jsDelivr: https://www.jsdelivr.com/documentation#id-publishing-packages