How to create JSX template engine from scratch
Tags: jsx, react, javascript, webdev
Published: 2022-11-23 09:51:22

# What is JSX and How Does it Work?

JSX is an XML-like syntax extension to JavaScript. It is similar to a template language, but it has the full power of JavaScript. JSX gets compiled to React.createElement() calls which return plain JavaScript objects called “React elements”. To get a basic introduction to JSX [see the docs here](https://reactjs.org/docs/introducing-jsx.html).

In this article, we will take a look at how JSX works and how to create custom components using JSX without using any frameworks.

{% youtube KgzFP5QxVrI %}

### Let's get started

#### Create a new Project

Create a directory for your project by running the following command in your terminal:

```bash
  mkdir jsx-demo
  cd jsx-demo
  npm init -y
```

---

#### Install Babel

Babel is a JavaScript compiler. It converts ECMAScript code into a backward-compatible version of JavaScript in current and older browsers or environments, Babel is used to compiling JSX to JavaScript.

```bash
  npm install --save-dev @babel/core @babel/cli @babel/plugin-transform-react-jsx
```

---

#### Create a `babel.config.js` file

```js
const presets = [];
const plugins = [
  [
    "@babel/plugin-transform-react-jsx",
    { runtime: "automatic", importSource: "./core" },
  ],
];

module.exports = {
  presets,
  plugins,
};
```

**NOTE:** The `importSource` option is used to specify the path to the module that will be used to create the JSX elements. In this case, we are using the `core` module. We will create this module in the next step.

---

#### Create a src directory in your project, This is where we will write our code.

```bash
  mkdir src
  cd src
  touch core/jsx-runtime.js
```

---

#### Let's create an `index.html` file in the root directory of our project

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>My App</title>
  </head>
  <body>
    <h1>Hello Friends</h1>
    <div id="root"></div>
  </body>
</html>
```

**NOTE:** The `id` of the root element is important. It is used to mount the application. We will see how to do this in the next step.

---

#### Let's create App.jsx and Button.jsx

We will create a `App.jsx` and `Button.jsx` files in the `src` directory. App.jsx will be the entry point for our project.

**Button.jsx**

```jsx
const Button = ({ children, onClick }) => (
  <button onClick={onClick}>{children}</button>
);
export default Button;
```

**App.jsx**

```jsx
import Button from "./Button";

const App = () => (
  <div>
    <Button onClick={() => alert(1)}>Click 11</Button>
    <Button onClick={() => alert(2)}>Click 12</Button>
    <Button onClick={() => alert(3)}>Click 13</Button>
  </div>
);

const rootElement = document.getElementById("root");
rootElement.appendChild(<App />);

export default App;
```

---

#### Let's add the babel script in package.json

This script will compile our JSX code to JavaScript code. The output will be in the `dist` directory.

```json
"scripts": {
    "build-babel": "babel src -d dist",
},
```

---

**Let's compile our code**

```js
    npm run build-babel
```

**Let's see how it looks like.**

```js
import Button from "./Button";
import { jsx as _jsx } from "./core/jsx-runtime";
import { jsxs as _jsxs } from "./core/jsx-runtime";

const App = () =>
  _jsxs("div", {
    children: [
      _jsx(Button, {
        onClick: () => alert(1),
        children: "Click 11",
      }),
      _jsx(Button, {
        onClick: () => alert(2),
        children: "Click 12",
      }),
      _jsx(Button, {
        onClick: () => alert(3),
        children: "Click 13",
      }),
    ],
  });

const rootElement = document.getElementById("root");
rootElement.appendChild(_jsx(App, {}));
export default App;
```

**NOTE:**

1. The `jsx` function is imported from the `core` module. We added the `babel.config.js` file, but we have not created `jsx-runtime` yet. We will create it in the next step.
2. JSX code is converted to JavaScript function, Which will call the `jsx` function.

---

#### Most important part of this article is creating the `jsx-runtime` module. This is where the magic happens.

All the components import the `jsx` function from the `jsx-runtime` module. The `jsx` function is responsible for creating the elements.

```js
const add = (parent, child) => {
  parent.appendChild(child?.nodeType ? child : document.createTextNode(child));
};

const appendChild = (parent, child) => {
  if (Array.isArray(child)) {
    child.forEach((nestedChild) => appendChild(parent, nestedChild));
  } else {
    add(parent, child);
  }
};

export const jsx = (tag, props) => {
  const { children } = props;
  if (typeof tag === "function") return tag(props, children);
  const element = document.createElement(tag);
  Object.entries(props || {}).forEach(([name, value]) => {
    if (name.startsWith("on") && name.toLowerCase() in window) {
      element.addEventListener(name.toLowerCase().substr(2), value);
    } else {
      element.setAttribute(name, value);
    }
  });
  appendChild(element, children);
  return element;
};

export const jsxs = jsx;
```

Let's understand how the `jsx` function works.

As you can see, the `jsx` function takes two arguments, the first argument is the tag name and the second argument is the props. The `jsx` function returns a plain JavaScript object. If the tag is a function, it will call the function and return the result. If the tag is a string, it will create a DOM element, set all the props to the element, and return the element.

---

#### The last step creates a bundle file using webpack And injects the bundle file into the `index.html` file.

Install webpack, webpack-cli and html-webpack-plugin using the following command:

```js
    npm install --save-dev webpack webpack-cli html-webpack-plugin
```

**NOTE:** We can also configure babel in the webpack config, But I'm using babel separately to get a better understanding of how JSX code is compiled.

---

#### Create a webpack.config.js file

Add the following code to the `webpack.config.js` file.

```js
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./dist/App.js",
  mode: "production",
  output: {
    path: `${__dirname}/build`,
    filename: "bundle.js",
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "src/index.html",
    }),
  ],
};
```

**NOTE:** The `entry` is the path of the compiled file. The `output` is the path where the bundle file will be created. The `template` is the path of the `index.html` file.

---

#### Update package.json scripts

```json
"scripts": {
  "start": "webpack serve --open",
  "build-babel": "babel src -d dist",
  "build-webpack": "webpack --config webpack.config.js",
  "build": "npm run build-babel && npm run build-webpack"
},
```

**NOTE:** The `start` script will start the webpack dev server. The `build` script will compile the code using babel and create the bundle file using webpack.

---

#### Time to build our project

```js
    npm run build
```

After running the above command, you will see a `build` directory in your project. This is where the compiled code will be saved. Also, the dist directory will have the compiled code. We can delete the dist directory if we want, I would recommend keeping it for reference.

---

#### Run the project

```js
    npm run start
```

Quick Tip: You can use serve package to start the HTTP server from any directory. This is very useful when you are working on a static website.

```js
    cd build
    npx serve
```

---
Live Demo: [Here](https://stackblitz.com/edit/how-to-create-the-app-using-jsx-without-react-devsmitra?file=package.json)

In the next article, We will also add a state to this app.

Thank you for reading 😊

Got any additional questions? please leave a comment.

---

##### Must Read If you haven't

{% link  https://dev.to/devsmitra/3-steps-to-create-custom-state-management-library-with-react-hooks-and-context-api-1bfh %}
{% link  https://dev.to/devsmitra/how-to-cancel-javascript-api-request-with-abortcontroller-36cm %}
{% link  https://dev.to/devsmitra/getting-started-with-solidjs-a-beginners-guide-5af4 %}

---

###### More content at [Dev.to](https://dev.to/devsmitra "Rahul Sharma(DevsMitra)").

{% user devsmitra %}

###### Catch me on
<p>
{% cta https://www.youtube.com/@bit-buddy?sub_confirmation=1 %} Youtube {% endcta %}
{% cta https://github.com/devsmitra %} Github {% endcta %}
{% cta https://www.linkedin.com/in/rahul-sharma-18b8b374 %} LinkedIn {% endcta %}
{% cta https://devsmitra.medium.com %} Medium {% endcta %}
{% cta https://stackblitz.com/@devsmitra %} Stackblitz {% endcta %} {% cta https://devsmitra.hashnode.dev %} Hashnode {% endcta %}
{% cta https://hackernoon.com/u/devsmitra %} HackerNoon {% endcta %}
</p>