What is JSX and how to build a jsx from the scratch?
/ 6 min read
Last Updated:Disclaimer: The following content is intended for learning purposes. While inspired by React, the goal is to convey the fundamental concepts of building a library similar to React at a foundational level.
Hi guys,
JSX, or JavaScript XML, was brought to us by Facebook back in 2013 with React. Since then, many people have shared what JSX is and how to use it. Today, let’s dive into something fun, creating our custom JSX extension.
- Part 1 - JSX and virtual DOM
- Part 2 - Custom state management
- Part 3 - VirtualDOM: Optimize rendering process
What is JSX?
Before we jump in, I hope you’re already familiar with using JSX in React. If not, no worries, here’s a quick explanation: JSX is a syntax extension that looks like HTML but is actually JavaScript and it looks like this:
How does JSX work?
Browsers don’t understand JSX syntax right out of the box. We need a tool to turn our JSX code into plain JavaScript that browsers can run. This tool is called a transpiler, and one of the most popular ones is Babel.
Babel is a JavaScript compiler that converts JSX code into standard JavaScript that works with the React framework.
For example:
Gets transpiled into this JavaScript code:
You can try it by yourself with Babel REPL!
After that, React will attach the resulting elements to the root element.
This code renders our element to the DOM:
Alright, now we have a basic understanding of how React renders the DOM.
But what’s really happening behind the scenes? To find out, let’s build our own custom JSX extension.
Let’s Build Our Own JSX Extension
We won’t be building a custom transpiler from scratch. Instead, we’ll use Babel. We’ll also use ParcelJs, a JavaScript compiler built on SWC. Parcel supports JSX right out of the box, and JSX is automatically enabled in .jsx or .tsx files.
First, we need to install the necessary dependencies:
Your package.json
file should look like this:
Babel Configuration
Next, we need to set up Babel. Create a .babelrc
file in the root of your project with the following settings:
There are two key options here: importSource
and runtime
- importSource allows us to set a custom module from which Babel will import the necessary functions (jsx, jsxs, Fragment) when using the automatic runtime. For example, with the above settings, Babel will do:
- runtime decides which runtime to use. It can be
classic
orautomatic
. In short,classic
requires manually imports JSX functions, whileautomatic
handles the imports for you from the specifiedimportSource
.
Example with the classic
option:
As you can see, you have to manually import React.createElement
or custom pragma functions import { createElement } from './utils/createElement'
for each file.
Using the automatic
runtime with a custom importSource
allows Babel handle the imports automatically.
For more options, see here.
JSX Functions
Alright, let’s define the necessary functions for the automatic
runtime:
Here’s a detailed explanation of each function:
createElement
function creates a representation of a virtual DOM element, similar to React.createElement.Fragment
helps us group list of children without adding extra nodes.jsx
function is used by the JSX transpiler (Babel) when the runtime is set to automatic.jsxs
similar tojsx
, but intended for elements with multiple children.
Next, we’ll need to add a function to convert a virtual DOM tree into actual DOM nodes and append them to a specified parent node.
Render our Virtual DOM
Next, let’s add the ./src/App.mjs
component:
And ./src/index.mjs
to import everything and render the components:
Finally, let’s add ./src/index.html
:
And that’s all there is to it! Let’s give it a try. Run npm run start
and open http://localhost:4321
in your browser. You’ll see the text Hello JSX
displayed.
Conclusion
We’ve successfully built a simple JSX extension to render both complex and simple components. We’ve explored how JSX is parsed and utilized, and we’ve created functions to construct a tree structure, known as virtual DOM, then converted into an actual DOM tree for the browser to render.
However, at this stage, we don’t yet have the ability to dynamically re-render specific elements in the virtual DOM, manage state, or use hooks. This is just the beginning. Stay tuned for more in-depth coverage of these features in future articles.
Thank you for reading 😊
And I go to write the next article.