React: Bug: Cannot import 'react/jsx-runtime' from esm node/webpack 5

Created on 12 Nov 2020  路  11Comments  路  Source: facebook/react

React version: 17.0.1

Steps To Reproduce

  1. Create a new directory, cd to it.
  2. Run npm i react@17 webpack@5 webpack-cli@4
  3. Create index.mjs with the following content:
    js import * as jsx from 'react/jsx-runtime'; console.log(jsx);
  4. Run node index.mjs
  5. Run npx webpack-cli path/to/index.mjs

    • I had to use an absolute path on my machine or webpack-cli wouldn't find index.mjs, don't know why.

Link to code example: --

The current behavior

> node index.mjs
node:internal/process/esm_loader:74
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'D:\repos\jsx\node_modules\react\jsx-runtime' imported from D:\repos\jsx\index.mjs
Did you mean to import react/jsx-runtime.js?
    at new NodeError (node:internal/errors:277:15)
    at finalizeResolution (node:internal/modules/esm/resolve:307:11)
    at moduleResolve (node:internal/modules/esm/resolve:742:10)
    at Loader.defaultResolve [as _resolve] (node:internal/modules/esm/resolve:853:11)
    at Loader.resolve (node:internal/modules/esm/loader:85:40)
    at Loader.getModuleJob (node:internal/modules/esm/loader:229:28)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:51:40)
    at link (node:internal/modules/esm/module_job:50:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}
> npx webpack-cli D:\repos\jsx\index.mjs
[webpack-cli] Compilation finished
assets by status 264 bytes [cached] 1 asset
./index.mjs 64 bytes [built] [code generated]

ERROR in ./index.mjs 1:0-41
Module not found: Error: Can't resolve 'react/jsx-runtime' in 'D:\repos\jsx'
Did you mean 'jsx-runtime.js'?
BREAKING CHANGE: The request 'react/jsx-runtime' failed to resolve only because it was resolved as fully specified
(probably because the origin is a '*.mjs' file or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

webpack 5.4.0 compiled with 1 error in 150 ms
npm ERR! code 1

The expected behavior

No issues importing react/jsx-runtime with no file extensions.

I can think of two solutions:

Needs More Information Unconfirmed

Most helpful comment

I'm reposting this comment of mine from the Babel's tracker but I adjust it slightly to fit better here. You can read it here or in the linked thread.


Adding extensions in the emitted code is being problematic for a couple of reasons and I believe that it should be avoided. I understand that it's unfortunate that things break right now because of it - but node's semantics are very new. The ESM support in node has been released just this month - so it's understandable that some packages are not yet ready in full for it.

The solution for this would be indeed to add exports map like in this PR: https://github.com/facebook/react/pull/20304 . It was already a great gesture from the React team to ship runtimes for all~ React versions. I suppose they could add exports map in a similar fashion to all of them, just to avoid confusion etc. It makes sense given how many users they have but it's not up to me to decide about this.

As to the current webpack's situation - just don't use .mjs for now. If you rename your file to .js then webpack will gladly resolve the react/jsx-runtime entrypoint. You just don't have to opt-into the new node's semantics right away and just give time for this issue here to be resolved.

All 11 comments

Same thing with react-dom/server:

> node .\index.mjs
node:internal/process/esm_loader:74
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'D:\repos\jsx\node_modules\react-dom\server' imported from D:\repos\jsx\index.mjs
Did you mean to import react-dom/server.js?
    at new NodeError (node:internal/errors:277:15)
    at finalizeResolution (node:internal/modules/esm/resolve:307:11)
    at moduleResolve (node:internal/modules/esm/resolve:742:10)
    at Loader.defaultResolve [as _resolve] (node:internal/modules/esm/resolve:853:11)
    at Loader.resolve (node:internal/modules/esm/loader:85:40)
    at Loader.getModuleJob (node:internal/modules/esm/loader:229:28)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:51:40)
    at link (node:internal/modules/esm/module_job:50:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Is this code created by the babel transform or did you manually add it?

The new JSX transform is not supposed to be used manually:

The functions inside react/jsx-runtime and react/jsx-dev-runtime must only be used by the compiler transform. If you need to manually create elements in your code, you should keep using React.createElement. It will continue to work and is not going away.

-- https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#whats-different-in-the-new-transform

The react/jsx-runtime import is added by the babel preset, I just showed a simplified example for quick bug reproduction.

It can be easily fixed with a simple exports map in package.json:

{
  "type": "commonjs",
  "exports": {
    ".": "./index.js",
    "./jsx-dev-runtime": "./jsx-dev-runtime.js",
    "./jsx-runtime": "./jsx-runtime.js",
    "./": "./"
  },
}

I just showed a simplified example for quick bug reproduction.

Could you show an example that uses the jsx-runtime as it's intended? That would help identify the solution better. For example, a fix could be applied to the transformer instead.

In the repo I work on, we've started publishing the library using the new runtime, using rollup+babel.
If you install it (npm install react-data-grid), and check node_modules/react-data-grid/lib/bundle.js, you'll see the import:

import { jsxs, jsx, Fragment } from 'react/jsx-runtime';

Some bundlers like webpack 4 or rollup are fine with it, but webpack 5 adopted the newer Node semantics regarding esm, so it expects either a full path with file extension, or react/jsx-runtime to be matched in an exports map.
It's also an issue in Node if you try to do server-side rendering from an esm file.

IMO adding an exports map should be a quick and safe enough fix.
There are multiple transformers that would need fixing otherwise: Babel, TypeScript 4.1, more?
I can go back to the classic runtime so it's not a major issue, but it needs to be fixed upstream one way or the other eventually.

you'll see the import

Could you share a minimal reproduction that produced this code?

I understand that this might seem frustrating since you've already identified a solution that works for you. But the code you're proposing has to be maintained by other people as well so they need to understand what problem it tried to solve. If that problem contains code that is not supposed to be used in that way, then it becomes harder to reason about the fix.

I've set up a minimal repo that'll generate similar bundles:
https://github.com/nstepien/react-20235
Let me know if this is good enough.

I faced the same problem by using creat-react-app.

Looks like the exports field got added in #20304, which should resolve this issue.
@sebmarkbage When can we expect a patch release for react 17? It would be nice to backport the exports field to react 16 as well.

For anyone running into this issue, until a fix is released, I also encountered this, and found a way to resolve it.

In my case, I have a few React components exposed from internal packages published to our npm repo. Each component is written in TypeScript, and the published package contains the transpiled files, which already contain the react/jsx-runtime import.
These components are then used in a React application, also written in typescript, which is then compiled using webpack v4.
When running webpack for the application, I got the Module not found: Error: Can't resolve 'react/jsx-runtime' in '.../app/node_modules/@components/Component/src/index' error, similar to the one in this issue.

To fix this, I added 'react/jsx-runtime': require.resolve('react/jsx-runtime'), to my webpack configuration's resolve.alias field.

I'm reposting this comment of mine from the Babel's tracker but I adjust it slightly to fit better here. You can read it here or in the linked thread.


Adding extensions in the emitted code is being problematic for a couple of reasons and I believe that it should be avoided. I understand that it's unfortunate that things break right now because of it - but node's semantics are very new. The ESM support in node has been released just this month - so it's understandable that some packages are not yet ready in full for it.

The solution for this would be indeed to add exports map like in this PR: https://github.com/facebook/react/pull/20304 . It was already a great gesture from the React team to ship runtimes for all~ React versions. I suppose they could add exports map in a similar fashion to all of them, just to avoid confusion etc. It makes sense given how many users they have but it's not up to me to decide about this.

As to the current webpack's situation - just don't use .mjs for now. If you rename your file to .js then webpack will gladly resolve the react/jsx-runtime entrypoint. You just don't have to opt-into the new node's semantics right away and just give time for this issue here to be resolved.

Was this page helpful?
0 / 5 - 0 ratings