Webpack not prioritizing the pkg.module for dependencies

Do you want to request a feature or report a bug?


What is the current behavior?

Webpack prioritizes the browser field in a package.json, over the module field (if both are passed)


this.set(“resolve.mainFields”, “make”, (options) => {
if(options.target === “web” || options.target === “webworker”)
return [“browser”, “module”, “main”];
return [“module”, “main”];

If the current behavior is a bug, please provide the steps to reproduce.

Created two gists, a module and a consumer. They are available here:

Repro Steps:

  1. git clone https://gist.github.com/jshcrowthe/64ec07274e4ac966b3e116078c586dfa
  2. cd 64ec07274e4ac966b3e116078c586dfa
  3. npm install
  4. npm start
  5. Open browser and navigate to webpack-dev-server URL
  6. Observe console logs, should look something like the following:

screen shot 2017-09-13 at 12 01 58 pm

What is the expected behavior?

Webpack should prioritize the pkg.module field over pkg.browser. Browserify also relies on the pkg.browser but does not have support for ES Modules. Seeing as webpack is able to more intelligently parse the ES Module bundles, it should prioritize that bundle if available.

NOTE: I am aware that you can change the resolve.mainFields to modify this however you’d like. I’m suggesting that the default behavior should be changed

If this is a feature request, what is motivation or use case for changing the behavior?


Please mention other relevant information such as the browser version, Node.js version, webpack version and Operating System.

  • Webpack: 3.5.6
  • Node.js: 8.4.0
  • NPM: 5.4.1
  • Yarn: 1.0.1
  • OS: macOS 10.12.6

Author: Fantashit

2 thoughts on “Webpack not prioritizing the pkg.module for dependencies

  1. @sokra thanks for the response! Would you be willing to explain your rationale behind:

    browser seem to be more specific for in-browser usage, while module is more generic for all targets.

    Based on the current state of ES modules in Node (which I understand is still subject to change), I would disagree with that statement. Here’s what I’m thinking (tell me if you think I’m crazy):

    As a library author I have a bunch of environments that I’d want to target. For the context of this issue, the ones I’m thinking of are:

    1. Browser – minified standalone JS executable
    2. Browser – via CJS
    3. Browser – via ESM
    4. Node – via CJS
    5. Node – via ESM

    Now we don’t have enough specced fields to cover all of these entrypoints (the only specced one is main), so the community has come up with some non-standard ones (i.e. browser, module, jsnext:main, etc). As I’m sure you’re already aware, different bundlers support these non-standard fields to different degrees, but the main, browser, and module fields seem to be agreed on by the big players (i.e. Browserify, Webpack, Rollup, am I missing more that I should be aware of?).

    Library author: if my goal is to provide entrypoints for all of these environments, I can do so by doing the following:

    1. Browser(minified standalone JS executable) – This file would typically be thrown on a CDN and served alone. It is contained in the package, but not explicitly listed as an entrypoint in the package.json. Users can npm install our package and directly reference the node_modules dir via script tag. They can also access this binary via a CDN service like https://unpkg.com/

    2. Browser(CJS) – This file would be attached to the browser field of the package.json. Webpack and Browserify support this out of the box, and rollup supports it via plugin.

    3. Browser(ESM) – This file would be attached to the ‘module’ field of the package.json. Webpack and Rollup support this and are able to do static optimizations to the resulting bundles they generate. AFAICT, Browserify is still waiting for Node support to be resolved before they act (see: browserify/browserify#1186)

    4. Node(CJS) – This file would typically be attached to the main field of the package.json. Optionally, this can be omitted in favor of an index.js at the root of the package.

    5. Node(ESM) – Per this section of the ESM draft, you could accomplish this by doing the following:

      • By providing an entrypoint of the same name as your CJS node entrypoint but with the .mjs extension and changing the package.json main field as demonstrated below:
           "main": "path/to/file" // previously: "main": "path/to/file.js"
      • By providing an index.mjs, alongside a index.js, at the root of the package and omitting the main field entirely from the package.json.

    Consumer of a library: I would think one would always want to get the ES Modules version of a library where available, and fallback to the other versions. We currently have a clean way to delineate between the two* and prioritizing the ES Module build benefits Webpack users by ultimately reducing bundle size, and allowing for auto chunking of dynamic imports (further reducing blocking bundle size).

    *: pkg.browser/pkg.module for browser envs and main or index.js/index.mjs for Node

    The hard part as a library author is, though Webpack supports the reprioritization of the resolve.mainFields, it is easier to expect all of my users to update Webpack, than it is to ask them all to add and maintain additional code in their Webpack config.


    EDIT: P.S. I’m a tad long-winded, thanks in advance for your understanding 🙂

  2. Does WebPack get used for node code?


    In my opinion it’s wrong to publish browser-only code at the module field. The module field doesn’t say anything about the target enviroment. It’s like main but with the only difference that ESM is used.

    The browser is a field which can be used to provide a browser-only replacement for the package.

    If module has higher priority than browser people would publish browser-only code to module, but this will break non-browser builds with ESM support (like webpack compiling for node.js, electron, …).

    It’s possible to publish different code for all targets with this package.json:

      "main": "lib/index",
      "module": "lib/index.mjs",
      "browser": {
        "./lib/index.js": "./lib/index.browser.js",
        "./lib/index.mjs": "./lib/index.browser.mjs"
    1. Browser – via CJS -> lib/index.browser.js
    2. Browser – via ESM -> lib/index.browser.mjs
    3. Node – via CJS -> lib/index.js
    4. Node – via ESM -> lib/index.mjs

    Note: It’s probably better if you put all environment-specific code in a module and only replace this one with the browser field.

Comments are closed.