Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Console error 'Uncaught ReferenceError: React is not defined' when using Rollup.js + React 18

I am getting this console error in the Chrome browser

index.tsx:13 Uncaught ReferenceError: React is not defined
    at index.tsx:13:2

I am really struggling to figure out how to get Rollup.js to work with React. I've been able to write a good-sized app in pure js using Rollup.js, but I want to start using React with it now. I'm not sure how to resolve this issue ; the code simply won't run in the browser yet I have no compilation issues. I've done much searching on this topic and none of the suggestions work. Any help would be appreciated!

I am compiling to bundle.js using

rollup -c -w

The output from rollup is

rollup v3.6.0
bundles src/ts/index.tsx → resources/public/js/bundle.js...
LiveReload enabled
created resources/public/js/bundle.js in 1.1s

[2023-01-24 01:11:33] waiting for changes...

My rollup.config.js looks like

import typescript from '@rollup/plugin-typescript';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import babel from '@rollup/plugin-babel';

export default {
  input: 'src/ts/index.tsx',
  output: {
    file: 'resources/public/js/bundle.js',
    format: 'iife',
    sourcemap: 'inline',
    inlineDynamicImports: true,
    globals: {
      "react/jsx-runtime":"jsxRuntime",
      "react-dom/client":"ReactDOM",
      "react":"React"},
  },
  external: [ 'react', 'react/jsx-runtime','react-is', 'react-dom/client','react-dom'],
  watch: true,
  plugins: [typescript(),
        nodeResolve(),
        livereload(),
        babel({
          babelHelpers: "bundled",
              exclude: 'node_modules/**',
              presets: [ ["@babel/preset-react", {"runtime": "automatic"}] ]
        })]
};

My tsconfig.js

{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react",
    "noImplicitAny": true,
    "removeComments": true,
    "checkJs": false,
    "preserveConstEnums": true,
    "sourceMap": true,
  },
  
  "include": ["src"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

My index.tsx file:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

App.tsx

import React from 'react';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
like image 594
user600830 Avatar asked Sep 24 '26 20:09

user600830


1 Answers

I figured this out myself and I wanted to post the solution as I was doing a lot of head scratching. I hope this helps future readers. ChatGPT was my co-pilot and helped me arrive at the solution.

Because React is compiled as a commonjs aka cjs module, you need the @rollup/plugin-commonjs installed. My understanding is that there is a particular order you need to call the plugin in the plugins array. commonjs must come before babel.

In addition, you will need the @rollup/plugin-replace to replace the process.env.NODE_ENV var in the root index.js of the React library with a value so that you can choose the minified or non-minified version.

'use strict';

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

In my case, my rollup.config.js is for a dev environment so I set the value to dev, otherwise I would replace it with production. To be on the safe side, I call the replace plugin before commonjs or babel.

I also removed the external declarations I had above

Working rollup.config.js

import typescript from '@rollup/plugin-typescript';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import replace from '@rollup/plugin-replace';

export default {
  input: 'src/ts/index.tsx',
  output: {
    file: 'resources/public/js/bundle.js',
    format: 'iife',
    sourcemap: 'inline',
    inlineDynamicImports: true,
    globals: {
      "react/jsx-runtime":"jsxRuntime",
      "react-dom/client":"ReactDOM",
      "react":"React"},
  },
  watch: true,
  plugins: [typescript(),
            nodeResolve(),
            livereload(),
            // this should be 'production' for minified code
            replace({preventAssignment: true,
             'process.env.NODE_ENV': JSON.stringify('dev')}),
            commonjs(),
            babel({
            babelHelpers: "bundled",
               exclude: 'node_modules/**',
               presets: [["@babel/preset-react", {"runtime": "automatic"}]]})]};
like image 146
user600830 Avatar answered Sep 26 '26 21:09

user600830