javascriptbrowseres6-modules

Not using require but still getting "require is not defined"


I'm getting the error message:

(global) - index.js:1:0
ReferenceError: require is not defined

The solution I keep seeing online is to use ES6 modules and export/import rather than using require. However, I'm not using require, at least not in any way that's obvious to me.

Is it just not possible to do a fully client-side React application this way? If it is possible, what do I need to change to make this work?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" />

    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>

    <title>Title</title>
</head>
<body>
    <div id="root"></div>
    <script src="index.js" type="text/babel"></script>
</body>
</html>
// index.js
import { SettingsFloater } from "./components/settingsFloater";

const App = () => {
    return (
        <div>
            <SettingsFloater />
        </div>
    );
};

ReactDOM.render(<App />, document.getElementById('root'));
// SettingsFloater.js
export const SettingsFloater = () => {
    return (
        <div></div>
    );
}

Solution

  • What’s happening is Babel Standalone is rewriting your import/export into CommonJS (require(...)), because the code is being compiled as a “script” (not a browser ES module). Browsers do not provide require, so you get:

    ReferenceError: require is not defined
    

    So you are “using require” just indirectly, via Babel’s module transform.

    your setup triggers this because You load index.js with type="text/babel", Babel Standalone transpiles it in the browser, By default (or depending on presets/plugins), Babel converts ES modules to CommonJS and you know commonJS uses require, which doesn’t exist in the browser.

    So I'd say Use a real ESM setup (no Babel-in-browser), This is the cleanest “fully client-side” approach, but you must avoid JSX unless you have a build step.

    index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Title</title>
      </head>
      <body>
        <div id="root"></div>
        <script type="module" src="./index.js"></script>
      </body>
    </html>
    

    index.js (ESM + no JSX)

    import React from "https://esm.sh/react@17";
    import ReactDOM from "https://esm.sh/react-dom@17";
    import { SettingsFloater } from "./components/SettingsFloater.js";
    
    const App = () =>
      React.createElement("div", null, React.createElement(SettingsFloater));
    
    ReactDOM.render(React.createElement(App), document.getElementById("root"));
    

    components/SettingsFloater.js

    import React from "https://esm.sh/react@17";
    
    export const SettingsFloater = () => {
      return React.createElement("div", null);
    };
    

    Also note that You must run this from a server (not file://). Use any static server and also Include file extensions in imports (./components/SettingsFloater.js), because browsers require it for ESM.

    If you have the experience, Use a bundler (Vite)

    If you want JSX, multiple files, imports that “just work”, fast reload. then just use Vite ( will still be a fully client-side React app when deployed. the bundler is just for development/build).

    And Yes, a fully client-side React app is possible