Frontend Architecture
Building React from Scratch
A from-scratch reimplementation of React and ReactDOM, plus a custom Babel JSX transform, so you can see exactly what happens between writing JSX and a node showing up in the DOM.
On this page
We write JSX every day and trust the framework to turn it into pixels. The best way to stop treating that as magic is to rebuild it. So I wrote react-clone: a tiny version of React and ReactDOM, plus my own Babel plugin to compile the JSX. It’s small enough to read top to bottom.
There are three moving parts, and together they cover the whole mental model of React.
flowchart LR A[JSX source] -->|jsx-dansform transform| B[createElement calls] B --> C[Virtual element tree] C -->|render then mount| D[Real DOM nodes]
1. JSX is just function calls
JSX isn’t part of JavaScript. A compiler rewrites it before the browser ever
sees it. Instead of reaching for the official transform, I wrote a Babel plugin
called jsx-dansform that walks every JSXElement node and rebuilds it as a
createElement call, constructing the AST by hand with Babel’s type helpers. So
this:
function App() {
return <h2 style={{ color: 'yellow', textTransform: 'uppercase' }}>Test</h2>;
}
becomes a plain function call:
createElement('h2', { style: { color: 'yellow', textTransform: 'uppercase' } }, 'Test');
2. createElement builds a virtual tree
createElement never touches the DOM. It just returns a plain object that
describes what you want. The whole react-clone package is basically this:
export function createElement(type, props, ...children) {
return {
type,
props: {
...props,
children,
},
};
}
Nest those calls and you get a tree of plain objects. That tree is the virtual DOM. It’s cheap to build, cheap to inspect, and has nothing to do with the real document yet.
3. render walks the tree into the DOM
react-dom-clone takes the tree and mounts it. For a host element it creates a
real node, copies the props across (with a special case for style objects),
then recurses into the children, appending text nodes for strings and numbers:
function mountHost({ type, props }) {
const node = document.createElement(type);
updateDOMProperties(props, node);
createInitialDOMChildren(props.children, node);
return node;
}
export function render(reactElement, domElement) {
domElement.appendChild(mount(reactElement));
}
And that’s the full round trip. JSX becomes createElement calls, those build a
tree of objects, and the tree becomes real DOM.
Takeaways
- JSX is just
createElementcalls, and writing the transform yourself proves it. - The virtual DOM is nothing more than plain objects describing the output you want.
- Rendering is a recursive walk from that tree down into real DOM nodes.
The full source is on GitHub if you want to clone it and step through the pipeline yourself.
Get the code
A minimal React + ReactDOM clone with a hand-written JSX transform.
@danielbeckdev
Practical, senior-level engineering. New videos with companion write-ups.