facebook/react

Feature request: Add a "module" entry in package.json to export ES2015 version of React

misterfresh opened this issue Β· 32 comments

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

What is the current behavior?
React ecosystem was promoting ES6 classes and modules since 2014 and many packages like react-router, redux and so on, have an "es" folder in the npm package with source code in ES2015 modules. Unless I am missing something, it is strange that React itself does not offer that option.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/84v837e9/).
Install react and try to import it in a browser with native modules enabled.

What is the expected behavior?
Have an "es" folder in the npm package with ES2015 modules source code, like most React ecosystem projects do. Allow to import react from ES2015 native modules to make developer workflow more simple.

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
All versions

The source code of react is still using a custom module system that is close to CommonJS and not using es2015 modules AFAICT. I assume this is why they also aren’t exposed.

At rollup, we were made aware of this issue because you exposed a bug in rollup-plugin-commonjs. in a way, thanks for that πŸ˜‰. But this would not have happened if react had had a module entry/ES6 export.

Given the recent improvements to rollup's tree-shaking algorithm (and there are more in the pipeline), at least for rollup users, exposing an ES6 bundle might have some positive effects on the bundle size depending on what react features are used. If you want to go there, such a bundle should ideally

  • only provide named exports
  • not create global variables

as this makes it easier for the tree-shaking algorithm to do it's work.

Even though I must admit I do no understand every aspect of react's build script, I would expect this could basically be solved by adding another build target with the correct format + possibly an entry point that has the right exports.

Btw. I noticed you are using a rather old version of rollup. If there is anything preventing you from updating, please file an issue! There is an interesting TODO comment here–is this still current 😜?

@lukastaegert are named exports and default ones different in any sense? default is just a named export AFAIK, only treated differently when imported.

@Andarist default export can not be changed in runtime. Named exports are bindings, so with let can be changed from file with them.

Maybe my comment about named exports was a little cryptic, so I will elaborate. I was referring to the not uncommon practice of exporting things twice from a file–once as a named export and once as a member of the default export. The latter is what often prevents tree-shaking for the named export even though the default export is not used. The reason is that in the exporting file, the default export still needs to be assembled and if this assembled object is included in the bundle–which can happen easily for various reasons–all its members will be included in the bundle.

This practice probably stems from the fact that bundlers like webpack and indeed rollup's CommonJS plugin do this themselves when converting CommonJS files. Other libraries that do this include lodash-es. When you remove the default export from node_modules, bundle size after tree-shaking can currently be nearly halved (more would be possible with future improvements).

What does this mean for react? If you make it possible that this

import {Component} from 'react'

can be interchanged with

import React from 'react'
const Component = React.Component

then you always need to assemble the React object which in most likelihood will break most of tree-shaking for the whole package. However I understand that for these decisions, ease of use should always be preferred to premature optimisation. But it is a point to consider especially if you plan to move to a native ES6 modules in the future (which in my opinion you should).

Ah, sure - this I understand and I'm aware of. I don't like though recommending only named exports as some kind of the rule of thumb. Way better is to just educate people about consequences of attaching things to the default one (hint hint your eslint plugin ;) )

However I understand that for these decisions, ease of use should always be preferred to premature optimisation. But it is a point to consider especially if you plan to move to a native ES6 modules in the future (which in my opinion you should).

Personally I do not see import {Component} from 'react' any harder to use, assuming it would be exported purely as named export (no attachment to the default one).

Migration to the newer export style could be easily run with a codemod and also a babel plugin could be created to allow people using class MyComponent extends React.Component {} syntax.

@TrySound just a note: a default export is like any other export and can be changed at runtime. It's the export default statement that is the complicated one.

An export default of named function declaration or named class can be reassigned by reassigning to the function/class name, but otherwise it appears non-reassignable. But you can always do a mutable default export by declaring let Identifier and doing export { Identifier as default }.

Not that I recommend doing it as immutability is good for you.

For now I can make it work by manually re exporting the named exports in a separate file, then bundling that file with Rollup.

alt text

I want to make it clear you're not going to get awesome benefits by "tree shaking" React. Most of the code is in the internals that is always used. You might at most shave off one or two kB, and only if your code never uses React.Children.

So this is not a high priority optimization for us for that reason.

For me, the main benefit of having an ES module option for React is to be able to use native modules during development, to avoid rebundling on every change to the code.

Also with es modules in place webpack could scope hoist react, at the moment it simply bails out on react because it treats it as commonjs.

There's not much you'll gain from scope hoisting React, except for maybe a 1 KB of Children helpers.

For me, the main benefit of having an ES module option for React is to be able to use native modules during development, to avoid rebundling on every change to the code.

Why can't you use the regular UMD build in this case? It would also avoid rebundling.

This is blocked on deciding what we actually want to export from each package, and in which form: #11503
Feel free to comment with proposals there.

stale commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.

not stale

stale commented

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

If this issue is still affecting you, please leave any comment

any comment

luxp commented

Really want ESModule export, cause there is some cool things like vite, snowpack, etc need the esmodule support.

stale commented

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

any comment

stale commented

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

any comment

Waiting for ESModule export of react!

One more to that, waiting es modules

Really could use this being a thing. CDNs like JSDelivr's new ESM CDN are really cool, but they're not able to load React. As far as I know the only ESM CDN I know of that's able to work properly with React is the Skypack. Every other view library I know of works properly across the major CDN providers except for React :(

I've been experimenting recently with import-maps aided by the JSPM CDN and import-map generator, and am surprised and disappointed to find that react doesn't have a pure ESM build in its package (just cjs and umd). This is not about tree-shaking, bundling, or optimisation ... it's about choice, the choice to progress the state of JS in the browser, the choice to experiment with and embrace these new technologies such as import-maps, that have the very real potential to bring an end to the bundling madness.
Please, please, please, add a simple ESM build, along with appropriate 'module' & 'exports' fields into the package.json.

Hybrid (CommonJS and ESM) packages are too much hassle, e.g. dual package hazard which can easily break React, Styled Components, etc.

It's time to let CommonJS die and go all in on ESM. A number of packages have gone pure ESM and if React did too, it would be a huge signal to the ecosystem that CommonJS is on its last legs.

@steve-taylor Totally agree, but it's now 2023 and the issue has been open since 2017. I think we have to wait a few more years?

the issue has been open since 2017

Sorry but this is not a constructive comment. We all know for how long this issue is opened, it is not even an argument.
Please be patient or fix the issue by opening a PR