13

I'm using Babel-Standalone to use JSX in a React application without using NPM. Babel apparently translates 'import' statements into 'require' statements; importing 'require.js' and other dependencies to make this work produces more errors.

Surely, there must be a simple way to perform an import/export in the context of client-side JSX. Please advise (no Node/NPM/Webpack solutions are sought here; CDN of appropriate library(ies) and rewrite of import statement, etc., are sought).

<!doctype html>
<html lang="en-us">
<head>
    <title>React JSX Babel-Standalone Import/Export Problem</title>
    <meta charset="utf-8">
</head>
<body>
    <div id="root"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<script type="text/babel">

// See MyExport.js text below this SCRIPT
// Goal: to employ <MyExport /> in the return of App.

// import MyExport from "./MyExport";

const App = () => {
    return (
        <div>Hello</div>
    );
};
ReactDOM.render(<App />, document.querySelector("#root"));
</script>

<!-- MyExport.js:
const MyExport = () => {
    return (
        <div>MyExport</div>
    );
};
export default MyExport;
-->

</body>
</html>
Tom
  • 1,836
  • 4
  • 16
  • 30

3 Answers3

20

There is a solution: (1) The JSX script containing the export must be included in a SCRIPT element along with the others; it cannot simply be referenced by another script without. (2) Both that JSX script and the JSX script importing from it must have the custom attribute data-plugins="transform-es2015-modules-umd" along with the expected attribute type="text/babel". Run the following HTML, a modification of what was provided in the question, which provides the solution (you'll have to create MyExport.js locally to run it):

<!doctype html>
<html lang="en-us">
<head>
    <title>React JSX Babel-Standalone Import/Export Problem</title>
    <meta charset="utf-8">
</head>
<body>
    <div id="root"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script data-plugins="transform-es2015-modules-umd" type="text/babel" src="./MyExport.js"></script>
<script data-plugins="transform-es2015-modules-umd" type="text/babel">
import MyExport from "./MyExport";
const App = () => {
    return (
        <MyExport/>
    );
};
ReactDOM.render(<App />, document.querySelector("#root"));
</script>

<!-- MyExport.js:
const MyExport = () => {
    return (
        <div>MyExport element is imported!</div>
    );
};
export default MyExport;
-->

</body>
</html>

I hope this helps someone else.

Tom
  • 1,836
  • 4
  • 16
  • 30
  • 2
    This worked in babel-standalone v6. It does not work in babel-standalone v7, which doesn't know what a 'transform-es2015-modules-umd' is. – blerg Oct 18 '19 at 16:03
  • 1
    After hours of searching, this answer helped me get it working. Thank you! – Bilal Fazlani Dec 08 '19 at 17:41
  • 3
    @EvanThompson with it is now https://babeljs.io/blog/2017/12/27/nearing-the-7.0-release.html#renames-drop-the-year-from-the-plugin-name – John Duncan Aug 23 '20 at 19:18
  • 1
    @Tom Thanks, I had the same issue and it worked for me. But if I want to import yet another subcomponent into MyExport.js itself, then it doesn't work!! Any ideas? – IntelliData May 20 '21 at 16:10
  • I am also curious about the subcomponent imports. – Pangamma Mar 09 '23 at 00:19
  • 1
    As of 2023, I had to use `transform-modules-umd` instead of `transform-es2015-modules-umd`. (See the [Babel plugins list](https://babeljs.io/docs/plugins-list).) And note that (as in the example) the `data-plugins` must be placed on the component ` – Garret Wilson Mar 17 '23 at 21:37
  • Note also that if you want to use a `jsx` extension (e.g. `MyExport.jsx`), you'll need to use the form `import MyExport from "./MyExport.jsx"` (with the extension) or it will look for `MyExport.js`. If someone knows a way to leave the extension off but have it still find it, let me know. – Garret Wilson Mar 17 '23 at 21:42
  • Unfortunately this doesn't seem to work with JSX files that reference other JSX files, e.g. if `MyExport.jsx` uses `MyOtherExport.jsx`. Any ideas? – Garret Wilson Mar 19 '23 at 20:47
  • Indeed the Babel team has just [confirmed](https://github.com/babel/babel/issues/15504#issuecomment-1478640568) that Babel Standalone doesn't handle dynamically transpiling a JSX component in one file referencing a JSX component in another file. To me this was a show-stopper, so I bit the bullet and switched to full transpilation as part of the built process. I published a [blog post](https://www.garretwilson.com/blog/2023/03/21/no-fuss-react-jsx-with-spring-boot) explaining how JSX transpilation can be integrated into an existing Maven build. – Garret Wilson Mar 22 '23 at 14:34
7

you should include first all needed component files, then run the app js file Example:

<div id="root-react"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> 
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js" crossorigin></script>

And the file's tree is something like:

js/app.js
js/subcomponent.js

The app.js content is for example:

"use strict";

class MainReact extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
        <div>
            <strong>app.js</strong> is loaded<br/>
            <SubComponent />
        </div>
    )
  }
}

ReactDOM.render(<MainReact />, document.getElementById("root-react"));

subcomponent.js content:

"use strict";

class SubComponent extends React.Component {
    constructor(props) {
      super(props);
    }

    render() {
      return (
          <span> SubComponent-is-working </span>
        )              
    }
}

customElements.define('subcomponent', SubComponent);

The file inclusion in the html file should be:

<script type="text/babel" src="js/subcomponent.js"></script>
<script type="text/babel" src="js/app.js"></script>

Hope it helps somebody. CodePen demo

Georgi Nikolov
  • 1,155
  • 1
  • 11
  • 15
5

Babel isn't a module bundler or a module system implementation, babel is just a transpiler to provide access to the latest JS features that aren't supported in the browser or node. If you want to use ES Modules without any third parties like webpack, rollup, etc. have a look at https://developers.google.com/web/fundamentals/primers/modules.

You should be able to do something like:

<script type="module">
  import MyExport from "./url/to/MyExport.mjs";

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

  ReactDOM.render(<App />, document.querySelector("#root"));
</script>

JS Modules via script tags is only supported in the latest versions of major browsers: https://caniuse.com/#feat=es6-module

Also, you'll need to workaround the fact that babel-standalone needs your script tags to be text/babel EDIT: the workaround is using a data-type="module" tag as well as the type="text/babel" tag: https://babeljs.io/docs/en/babel-standalone#usage

George Kamar
  • 134
  • 1
  • 12
Daniel Conde Marin
  • 7,588
  • 4
  • 35
  • 44