Babel Tutorial Part 2 - React and JSX

Published: Thursday, August 13, 2020
Updated: Thursday, August 20, 2020

Greetings, friends! I hope you learned a lot in Part 1 of my Babel series. Today, I'd like to actually setup a React application using Babel but without Webpack. If you have ever used Create React App, then you should know that it uses Babel and Webpack internally.

First, I'll make an assumption that you have Node and npm installed on your computer already. If not, then please go to Node's homepage to install Node, which will then prompt you to install npm. Once these are installed, create a directory called babel-tutorial-react and navigate to it:

bash
Copied! ⭐️
mkdir babel-tutorial-react && cd $_

Then, you can run the following shortcut command to quickly bootstrap a new project with a package.json file:

bash
Copied! ⭐️
npm init -y

We need to install three Babel packages as dev dependencies: @babel/core, @babel/cli, and @babel/preset-react. We need @babel/core to orchestrate between the @babel/parser, plugins, @babel/generator. Using @babel/cli lets us use a CLI to transpile our code. We will use @babel/preset-react, which contains all the plugins we need to understand JSX and compile it down to React createElement methods.

bash
Copied! ⭐️
npm i -D @babel/core @babel/cli @babel/preset-react

Next, let's add the serve npm package to serve our assets from a small server.

bash
Copied! ⭐️
npm i -D serve

We will change our package.json file, so that we have a script named "start" that simply runs the command, "serve". Your package.json file should look like the following so far:

json
Copied! ⭐️
{
  "name": "babel-tutorial-react",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "serve"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.10.5",
    "@babel/core": "^7.11.1",
    "@babel/preset-react": "^7.10.4",
    "serve": "^11.3.2"
  }
}
warning
Your package.json file may look different if you are reading this article at a later date than when this was published. As of the time of this writing, the versions of each package you see above were the latest releases. Just make sure you're using Babel 7 if you're following along, and you should be fine 😄.

The next thing we want to do is to create a new html file named index.html that uses React from a CDN.

html
Copied! ⭐️
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Babel + React Demo</title>
</head>
<body>
  <h1>Babel + React Demo</h1>
  <div id="root"></div>
  <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="src/index.js"></script>
</body>
</html>

The <div id="root"></div> element will be replaced by our React code. As you may know, we need at least one root element to bootstrap our React application. I have also added a small script tag that loads a JavaScript file from src/index.js. Let's create a directory named src and a file inside it named index.js.

js
Copied! ⭐️
function Celebrate() {
  return <p>It's working! 🎉🎉🎉</p>
}

ReactDOM.render(
  <Celebrate />,
  document.getElementById('root'),
)

Alright, we added our React code! Let's see if it works! We'll start our server using npm run start (or using the alias, npm start). This will start a server on an unused port and serve up our files as static assets. Is our JSX working?

Babel plus React demo without using Babel

Nope, of course not! We didn't set up Babel yet! Is the console complaining? Yep, as expected. We need Babel to parse JSX and transpile it, so we can run proper JavaScript in the browser.

console error from unexpected less than token

It's always a good practice to setup a project using a Babel configuration file. It's recommended to name this file babel.config.json if you don't plan on changing your Babel config dynamically. If you do plan on changing this file or running a dynamic Babel config that may change upon every build of your project, then you should use a babel.config.js file instead, so you can control the Babel config through JavaScript. However, it's best to use babel.config.json, so that bundlers such as Webpack can cache the results of Babel safely.

We will create a babel.config.json file that uses the @babel/preset-react group of plugins:

json
Copied! ⭐️
{
  "presets": ["@babel/preset-react"]
}

Since using this preset is so common, Babel provides a shorter notation:

json
Copied! ⭐️
{
  "presets": ["@babel/react"]
}

Native JavaScript doesn't understand JSX syntax. Babel to the rescue! Let's add a script to our package.json file called build:

json
Copied! ⭐️
"build": "babel src --out-dir lib"

Alternatively, you can use a shorter syntax:

json
Copied! ⭐️
"build": "babel src -d lib"

This command will use @babel/cli to compile all files in the src directory using all the presets and plugins found in our Babel config file, babel.config.json, and output new files into a directory called lib.

Our finished package.json file should look like the following:

json
Copied! ⭐️
{
  "name": "babel-tutorial-react",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "serve",
    "build": "babel src --out-dir lib"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.10.5",
    "@babel/core": "^7.11.1",
    "@babel/preset-react": "^7.10.4",
    "serve": "^11.3.2"
  }
}

If you run npm run build, then you should see a lib directory be created with the transformed index.js file. If you look inside of it, then you can see that Babel has transpiled the JSX down to React methods:

js
Copied! ⭐️
function Celebrate() {
  return /* #__PURE__ */React.createElement('p', null, 'It\'s working! \uD83C\uDF89\uD83C\uDF89\uD83C\uDF89')
}

ReactDOM.render(/* #__PURE__ */React.createElement(Celebrate, null), document.getElementById('root'))

Don't worry about the /*#__PURE__*/ annotations. These are used with Webpack to help with tree-shaking. Babel also converted our three 🎉 emojis into three pairs of Unicode code units.

Now that we have our transpiled JavaScript, let's change our index.html file to import code from lib/index.js instead of src/index.js:

html
Copied! ⭐️
<script src="lib/index.js"></script>

If your server is still running from using the npm run start command, then all you have to do is refresh the page to see the result of your updated index.html file. If not, then you'll need to make sure to start the server again. With your transpiled code running, you should see your React code running successfully.

Babel plus React demo using Babel

Yes! 🎉 It's working! You have successfully used Babel to transpile JSX without using Create React App or Webpack. Babel plugins are awesome! It's important to keep in mind that the Babel parser already understands JSX. If you were to invent your own kind of syntax, the parser would have to be able to understand it. If for whatever reason, you want to modify the parser, you would need to fork @babel/parser and add a plugin to consume your custom parser.

tip
Fun fact! CodePen lets you select Babel as a JavaScript preprocessor to transpile JSX down to React createElement functions too!

If you want to download the finished code, you can check out my GitHub repository, or you can clone the repo using:

bash
Copied! ⭐️
git clone https://github.com/inspirnathan/babel-tutorial-react.git

Phew! Still with me? In Part 3, we will discuss how to add source maps to the project, so we can debug code by looking at the original source code instead of the transpiled code. Happy coding!

Resources