Part 1: how to build your own JS framework

Learn by building your own React clone

Mike Guoynes
6 min readMar 9, 2022

When I don’t understand something fully, I try to break things down. Step by step.

New things pop up all the time, so understanding the fundamental building blocks will set you apart because you’ll know how things actually work.

In order to understand how React works, let’s challenge ourselves and write our own React from scratch.

Game Plan

The high-level goal for this article is to build a semi-optimized React. You will learn how to use JSX independently of React and how to create your own virtual DOM and render it.

This is the 3-step pipeline we are building:

(1) JSX -> (2) VIRTUAL DOM -> (3) RENDERING

  1. Setup JSX: so that we can store Virtual DOM representations of the rendered DOM. As a bonus, JSX enables us to declaratively use JS methods such as .map((x) => ... in the template.
  2. Virtual DOM: Convert JSX to a Javascript representation of the actual rendered DOM tree. By implementing our owncreateElementwe will see how to convert JSX to JS data structures. The full representation of our rendered page will be stored in JS. This representation is known as the Virtual DOM.
  3. Rendering: Finally, the Virtual DOM representations are converted into real DOM elements and actually rendered on the page.

By the end of this series, you will become familiar with Virtual DOM, data binding, and change detection. You’ll see behind the scenes what is happening, and be on your way to becoming the most badass version of yourself. In the next article I will cover building useState hooks and change detection.

Step 1/3: Setup JSX

Let’s go ahead and knock this setup out.

I won’t attempt to explain each piece in-depth. If you wish to dig deeper, many sections below include links to deep dive resources.

1.1 Folder setup

Run these commands in your terminal to create a new folder, add typescript, and bundle with parcel. (Deep Dive: See here)

mkdir mini-react-series && cd mini-react-series && yarn add typescript parcel -D

1.2. Generate a TSconfig file for Typescript

tsc --init

  • if that command doesn’t work make sure you have typescript installed npm install typescript -g

1.3. Setup JSX

JSX is a syntax extension for JavaScript that lets you write HTML-like markup inside a JavaScript file. We use Babel to transpile JSX to HTML.

  • Install Babel

npm i @babel/plugin-transform-react-jsx

  • Update your babel config file to use our framework’s implementation of createElement. To do this, go to the .babelrc file and update the pragma property (as shown below) so that it uses our framework’s soon-to-be-created createElement.
{
"plugins": [
["@babel/plugin-transform-react-jsx", {
"pragma": "MiniFramework.createElement", // default pragma is React.createElement
"throwIfNamespace": false // defaults to true
}]
]
}

1.4. Run it

yarn parcel index.html

Step 2/3: Virtual DOM

Our rendering engine is based on JSX andcreateElement. But, what exactly is JSX andcreateElement and why do we need it?

The purpose of createElement is to convert JSX to our own data structure/copy of each DOM element. This is known as the “Virtual DOM”. (deep dive):

JSX -> Virtual DOM -> DOM

“Virtual DOM” is simply the DOM you keep in Javascript form for diffing and optimizing your change detection cycle. Think of it as your framework’s personal copy of the DOM in JS format.

2.1 Implement React’s createElement to store JS Virtual DOM

Define a JSX element in a tsx file index.tsx and let Babel handle the rest.

const jsxElement = (
<h1 className="greeting">
Hello, world!
</h1>
)

Babel will find each JSX element in all of our *.tsx files and call createElement()

After calling createElement a virtual DOM representation of the DOM element is returned vDOMElement and stored in a JS object.

const vDOMElement = createElement(
"h1",
{ className: "greeting" },
"Hello, world!"
);

As you can see above, there are 3 arguments to the createElement method:

  • Argument 1: is the tag name "h1"
  • Argument 2: is an object with all of the element’s properties. In this example, it is just the CSS class: { className: “greeting” },
  • Argument 3: Are the children nodes of the given JSX element. In this case, it is just a text node that says “Hello, world!”

2.2 Initialize our framework object in index.tsx

Now, let’s implement our owncreateElement that accepts these 3 arguments and converts JSX to the Virtual DOM.

Create a object that has the createElement method. I chose to call this framework MiniFramework :

As you can see, createElement returns an element that has 4 properties:

  • Property 1: the tag / JSX element
  • Property 2: the tag type
  • Property 3: props which holds the element’s properties such as the className or input values. The children are the child elements and we will iterate over them later and render each of them recursively.

Step 3/3: Rendering

Rendering is essentially the process of taking JSX elements with their properties and creating the actual DOM.

Virtual DOM -> Rendering

Let’s implement rendering so that we can take any Virtual DOM representation and actually show it on the screen.

3.1 Create a index.html file:

  • add a <div id=”app”></div> in index.html. This is the container element where the app will be rendered.
  • include a script link to the index.tsx file that we created earlier.

3.2 Implement render

Let’s implement our render method which will take 2 arguments:

const render = (frameworkEl: FrameworkEl, container: Element | null) => { .... }
  • Argument 1: the virtual DOM element to render frameworkEl.
  • Argument 2: the container element where everything will be rendered inside. (Recall that we created <div id=”app”></div> in index.html )

Here’s what each line does:

  • Create simple elements when JSX is just textlines 2–10: If we encounter a string or number, then all we need to do is create a text nodes with document.createTextNode(...).
  • Initialize a real DOM Element lines 11: we create a real element that we will apply properties to and render.
  • Take properties from JSX Element and Apply it to the real element lines 14–18: To make sure that theactualDOMElement has the same classes, ids, value, etc we will loop through the props of the JSX element and apply it to the actualDOMElement .
  • Repeat above for every child node lines: 26–30: recursively run through each child and do the steps above

Render it!

We made it, all that’s left is to call our render method. Concretely, here’s what it looks like when you callrender :

MiniFramework.render(vDOMElement, document.querySelector(“#app”));

Which is the same as React’s:

Here’s the Complete Code

Nice work! ✊🏽🥂🎉

To recap, we have completed this pipeline:

JSX -> VIRTUAL DOM -> RENDERING

Resources:

**COMING SOON Change detection & useState hook.**

🚧 🏗 ️🚧

In the next article, you will learn how to implement change detection and React’s useState hook.

Rather than re-rendering / replacing all elements, we will surgically replace only the element that changed when setState is called. We will use our Virtual DOM to compare and replace anything that changed.

Conclusion:

Thanks for reading. If you learned something, hit the follow button!

--

--

Mike Guoynes

Demystifying FE engineering. Published @ http://javascript.plainenglish.io (2.5M+ readers). Follow to level up and invest in your frontend skills.