← Back to Let's build a virtual DOM library!

Mounting our virtual DOM

Creating a virtual DOM tree was easy, but it's not very useful by itself. What we really want is turn our VDOM tree into a real DOM in a HTML document. Our goal is to write a function that takes a VDOM tree and mounts it to the real DOM. Using our example VDOM tree from the previous section, the result should look like this:

<div class="container">
<h1>Hello, World!</h1>
<p>We're building a <small>(really simple)</small> virtual DOM library.</p>
</div>

We'll start by writing a function that takes a virtual DOM node, vnode, and mounts it to an existing DOM node, parentDom.

function mount(vnode, parentDom) {
let domNode;
if (typeof vnode === "string" || typeof vnode === "number") {
// When the node is a string or number, we can insert a plain text node.
domNode = document.createTextNode(vnode);
} else {
// For vnode objects, we create a HTMLElement of the node's type.
domNode = document.createElement(vnode.type);
}
return domNode;
}

Once again, this code is surprisingly simple. The beauty of the virtual DOM is that the type property maps directly to the tagName parameter of the document.createElement function. This means that we can create a DOM node of the correct type with a single line of code. If the vnode is a string or number, we can create a plain text node with document.createTextNode.

So far this code only creates the DOM node based on vnode.type, but it doesn't do anything with vnode.props or vnode.children. Let's start by setting the props as attributes on the DOM node.

function mount(vnode, parentDom) {
let domNode;
if (typeof vnode === "string" || typeof vnode === "number") {
// When the node is a string or number, we can insert a plain text node.
domNode = document.createTextNode(vnode);
} else {
// For vnode objects, we create a HTMLElement of the node's type.
domNode = document.createElement(vnode.type);
if (vnode.props) {
// All of the props are set as attributes on the HTMLElement.
for (const prop in vnode.props) {
domNode[prop] = vnode.props[prop];
}
}
}
return domNode;
}

The virtual DOM's simplicity continues to shine here, because the props object maps directly to the DOM node's attributes. We don't need to translate any of the props; we can simply loop through them and set them as attributes on the HTMLElement.

The final piece of the puzzle is to mount all of the children, and any of their children, and so on until the entire tree has been mounted. We can do this by recursively calling the mount function for each child.

function mount(vnode, parentDom) {
let domNode;
if (typeof vnode === "string" || typeof vnode === "number") {
// When the node is a string or number, we can insert a plain text node.
domNode = document.createTextNode(vnode);
} else {
// For vnode objects, we create a HTMLElement of the node's type.
domNode = document.createElement(vnode.type);
if (vnode.props) {
// All of the props are set as attributes on the HTMLElement.
for (const prop in vnode.props) {
domNode[prop] = vnode.props[prop];
}
}
if (vnode.children) {
// Any children go through the same process recursively until we have
// mounted the whole tree.
vnode.children.forEach((child) => mount(child, domNode));
}
}
// When we're finished, append the new DOM node to the parent.
parentDom.appendChild(domNode);
return domNode;
}

And just like that, our function is finished! ... For now, anyway. Let's take a quick look at how we would use this function, and then try it out for real.

// Our "application" code is just a function that returns a VDOM tree.
function app() {
return createVNode("div", { className: "container" }, [
createVNode("h1", null, ["Hello, World!"]),
createVNode("p", null, [
"We're building a ",
createVNode("small", null, ["(really simple)"]),
" virtual DOM library.",
]),
]);
}
// We retrieve the VDOM tree by calling app() and mount it to an existing DOM node.
mount(app(), document.getElementById("app"));

The JSFiddle embed below shows this code in action. Our mount function does exactly what we expect, and renders our virtual DOM into the real DOM. You can click on the JavaScript tab to view our VDOM library code, and the HTML tab to view our "application" code.

Continue onto the next section to learn how to handle custom components.

Next: Handling styles →