The React 17 JSX transform seems more complicated and React-specific then the standard JSX transform, so I'm not sure it's something I'm going to include in esbuild's core. It should be very straightforward to support this using a plugin though. I'm still working toward plugin support but I'm getting close. You can subscribe to #111 for updates about plugin support.
React has an official blog post about this feature now.
There is also a detailed RFC describing some of the motivations for this change. I like this one:
Additionally, if we ever want to standardize more of JSX we need to start moving away from some of the more esoteric legacy behaviors of React.
I'd love to see e.g. Vue be able to "just work" with JSX without any weird CLI tools running additional transforms, which is currently the case.
None of this is high-urgency. These changes are still in-progress. But I think it is something to be considered down the road.
FYI the recently-released --inject
feature can be used to automatically insert an import of React into each file. There is more detail in the release notes.
I noticed the new --inject
feature, which looks pretty cool.
This solves some (but not all) of the "JSX automatic runtime", which is really part of an overall revision to how JSX itself is transpiled. For example:
Pass
key
separately from propsCurrently, key is passed as part of props but we'll want to special case it in the future so we need to pass it as a separate argument.
jsx('div', props, key)
Always pass children as props
In
createElement
, children gets passed as var args. In the new transform, we'll always just add them to the props object - inline.The reason we pass them as var args is to distinguish static children from dynamic ones in DEV. We can instead pass a boolean or use two different functions to distinguish them. My proposal is that we compile
<div>{a}{b}</div>
tojsxs('div', {children: [a, b]})
and<div>{a}</div>
tojsx('div', {children:a})
. Thejsxs
function indicates that the top array was created by React. The nice property of this strategy is that even if you don't have separate build steps for PROD and DEV, we can still issue the key warning and still pay no cost in PROD.
So the proposal requires changes to how JSX is transformed into function calls, which I assume requires changes to the esbuild
core.
@chowey It's too bad that React is trying to change how they use JSX considering there are hundreds of frameworks/libraries that use "standard" JSX of method(tag?, attrs?, ...children)
. I think it's great that esbuild can support all of these frameworks and that it doesn't try to bend over for React. Adding any framework-specific changes to a bundler feels like an easy way to grow the codebase and complexity.
It's great that React has a Babel plugin though, and especially considering React is always in development and the RFC you quoted isn't settled, the best option is likely telling esbuild to preserve (i.e not touch) JSX and then tell Babel to convert all JSX found in all output chunks afterwards.
Agreed. This upgrade is entirely optional. From the React blog post:
Upgrading to the new transform is completely optional, but it has a few benefits:
- With the new transform, you can __use JSX without importing React__.
- Depending on your setup, its compiled output may __slightly improve the bundle size__.
- It will enable future improvements that __reduce the number of concepts__ you need to learn React.
__This upgrade will not change the JSX syntax and is not required.__ The old JSX transform will keep working as usual, and there are no plans to remove the support for it.
To reiterate, support for the JSX automatic runtime is completely optional and will continue to be completely optional into the future.
Regarding changes to JSX, I would love for JSX to be simple and standardized, so that other non-React frameworks can use it too. So I don't mind a change to JSX provided it moves in that direction. I once tried to create a Vue app in JSX, without using Babel (which means no @vue/babel-preset-jsx). It was horrible. Vue's createElement
behaves differently than React's createElement
and requires all sorts of special handling. If JSX can converge on a standard that works for everyone, then that is a big plus.
Most helpful comment
FYI the recently-released
--inject
feature can be used to automatically insert an import of React into each file. There is more detail in the release notes.