Here is an updated answer for 2022.
I ran into this error using vitejs, which uses rollup for its production builds/bundling.
I have included a solution for rollup and vite projects.
A note on old answers and documentation
In docs and forums on the internet there are still a lot of old comments referring to rollup-plugin-commonjs
and namedExports
.
- The last release of
rollup-plugin-commonjs
was v10.1.0
in 2019-08-27
rollup-plugin-commonjs
was renamed to @rollup/plugin-commonjs
with v11.0.0
in 2019-12-21
namedExports
was removed from @rollup/[email protected]
in 2020-06-05
So anything that refers to rollup-plugin-commonjs
or namedExports
is outdated and not relevant to the current version of rollup
or @rollup/plugin-commonjs
.
Why we get this error
This error happens with a lot of React libs because they use dynamic require(...)
statements to choose between dev and production source files.
I'm using react
for this example, but it's the same idea in react-dom
and react-is
.
// node_modules/react/index.js
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
When rollup processes this file it can't work out that the exports come from another file, so it rollup thinks the exports are empty, and that is why we get an error like 'useState' is not exported by 'node_modules/react/index.js'
.
Solutions
Option 1: Make libraries external
Use this option if you don't want to include the dependency inside your bundle at all.
In this case, mark these libraries as external
, meaning they won't be processed by rollup or included in the bundle.
This can also reduce the size of your bundle and avoid accidentally bundling these libraries multiple times.
Any libraries marked as external will need to be made available in the runtime environment (i.e. they're available on the page at runtime, or you have another build step that can resolve the externals and provide them to your library).
Option 2: Use aliases to manually resolve the files
Use this option if you your dependencies should be included inside your bundle - i.e. you want React to be included inside your library bundle.
- Install @rollup/plugin-alias
$ npm install @rollup/plugin-alias --save-dev
- Configure aliases in your
rollup.config.js
.
// rollup.config.js
import alias from '@rollup/plugin-alias';
import * as _path from 'path';
const isProduction = process.env.NODE_ENV === 'production';
/*
Define any path here that triggers the "is not exported" error
This is not needed if the react libraries are marked as `externals` and excluded from the bundle.
This is only an needed because we **want** react to be included in the bundle.
*/
let pathReact = 'umd/react.production.min.js';
let pathReactDom = 'umd/react-dom.production.min.js';
let pathReactJsx = 'cjs/react-jsx-runtime.production.min.js';
if (!isProduction){
pathReact = 'umd/react.development.js';
pathReactDom = 'umd/react-dom.development.js';
pathReactJsx = 'cjs/react-jsx-dev-runtime.development.js';
}
module.exports = {
input: 'src/index.js',
output: {
dir: 'output',
format: 'cjs'
},
plugins: [
alias({
entries: [
{
find: /^react$/,
replacement: require.resolve(_path.join('react', pathReact)),
},
{
find: /^react-dom$/,
replacement: require.resolve(_path.join('react-dom', pathReactDom)),
},
{
/*
2022-03-11
https://github.com/vitejs/vite/issues/6215#issuecomment-1064247764
*/
find: /react\/jsx-runtime/,
replacement: require.resolve(_path.join('react', pathReactJsx)),
},
]
})
]
};
NOTE:
- This assumes you are using
NODE_ENV
environment variable to control production/development builds
- I'm aliasing to the
umd
react files; if you need to point to the cjs
build then adjust the alias path as necessary
Vitejs version
Here is the equivalent configuration for a vitejs project:
// vite.config.ts
import * as _path from "path";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig((env) => {
// mode: 'production' | 'development'
const mode = env.mode;
/**
* Fix build messages/errors like: `'useState' is not exported by 'node_modules/react/index.js'`
*/
let pathReact = "umd/react.production.min.js";
let pathReactDom = "umd/react-dom.production.min.js";
let pathReactJsx = "cjs/react-jsx-runtime.production.min.js";
if (mode === "development") {
pathReact = "umd/react.development.js";
pathReactDom = "umd/react-dom.development.js";
pathReactJsx = "cjs/react-jsx-dev-runtime.development.js";
}
return {
resolve: {
alias: [
// react alias fix
{
find: /^react$/,
replacement: require.resolve(_path.join("react", pathReact)),
},
{
find: /^react-dom$/,
replacement: require.resolve(_path.join("react-dom", pathReactDom)),
},
{
/*
2022-03-11
https://github.com/vitejs/vite/issues/6215#issuecomment-1064247764
*/
find: /react\/jsx-runtime/,
replacement: require.resolve(_path.join("react", pathReactJsx)),
},
// End react alias fix
],
},
plugins: [react()],
};
});