The Virtual DOM and JSX

The Virtual DOM

One of React.js’ most prominent selling points is its ability to quickly and efficiently update the DOM of a web page. This is because it utilizes a concept called the Virtual DOM. Although React popularized the Virtual DOM, the concept has been around before React’s debut.

The Virtual DOM sounds more complicated than it really is. It is simply a copy of the DOM that you create within your JavaScript code. On the initial load of a web page that is utilizing a Virtual DOM, the Virtual DOM will render to the web page DOM. After that initial render, if any changes are made to the virtual DOM, a new copy of it is created and compared to the previous copy of the virtual DOM. Any differences in the two copies are detected and update the actual DOM accordingly. (This process is called diffing.) Only that change will be reflected in the actual DOM.

This process is much more efficient than other practices that manipulate the actual DOM directly. The DOM is very slow compared to the fast speeds that we expect from a web browser. In addition, any time a change is made to the DOM, the entire container that the affected node lives in usually has to be re-rendered to reflect the the change, instead of only re-rendering the change itself in the manner that React does. The more the DOM has to be manipulated, the longer it takes to perform these changes. The virtual DOM allows the actual DOM to be updated only when and where necessary, leaving the container itself untouched.


React utilizes a script called JavaScript XML (JSX), which simply allows you to embedded HTML/XML-like syntax inside of JavaScript. JSX is then converted to the most browser-supported version of Javascript using the transpiler, Babel. (Babel currently transpiles to ES5.) Although the Babel and JSX combination are the most widely known and practiced, JSX is really only ‘syntactic sugar’, meaning that it allows you to invoke complicated code using syntax that is intuitive and easy to read.

Remember that a virtual DOM is only a copy of the actual DOM, not the real thing. Because of this, a virtual DOM can be represented in a variety of different ways. JSX allows us to easily create virtual DOM elements within our JavaScript code. Babel, the transpiler, knows how to interpret JSX code and create real DOM elements through React. Other language configurations, such as HyperScript, allow you to use syntactic sugar to create virtual DOM elements as well, but not as nicely as JSX.

An actual DOM element created in HTML may look something like this:

     <div id="hello-container">
<h1> Hello World </h1>

In JSX, you can create virtual DOM elements within Javascript in a similar way:

import React from 'react'
class Hello extends React.Component {
render () {
return (
<div id="hello-container">
<h1> Hello World </h1>

Babel then transpiles this code into a React object, this one being the React.createElement function:

React.createElement('div', {id:'hello-container'}, 
React.createElement('h1', {}, 'Hello World')

However, if you look into the react.js code, you see that React.createElement is just a vanilla JavaScript function that takes in arguments of element type (type), attributes such as id (config), and the children of the element, which could be text nodes, more elements, or both.

ReactElement.createElement = function (type, config, children) {
var propName;
// Reserved names are extracted
var props = {};
var key = null;
var ref = null;
var self = null;
var source = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
if (hasValidKey(config)) {
key = '' + config.key;
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
for (propName in config) {
if (, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
var childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
if ("development" !== 'production') {
if (Object.freeze) {
props.children = childArray;
// Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
if ("development" !== 'production') {
if (key || ref) {
if (typeof props.$$typeof === 'undefined' || props.$$typeof !== REACT_ELEMENT_TYPE) {
var displayName = typeof type === 'function' ? type.displayName || || 'Unknown' : type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
if (ref) {
defineRefPropWarningGetter(props, displayName);
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);

Here is a brief summary of what’s happening above:

After the arguments are passed in, the function determines if the element has any properties, and if so, determines what they are. It then counts how many children are passed in based on the number of arguments and stores them into an array. In addition to the properties passed into config, it also gets the default properties of that particular element. Lastly, it returns a virtual react element. It is not until the we run ReactDOM.render(…), that these virtual DOM elements are rendered to the actual DOM.

In summary, the virtual DOM is a concept that React utilizes, but is not just unique to React. Babel converts JSX to React objects, but React is just written in vanilla JS, and therefore can be written many different ways to create virtual DOM elements.

Like what you read? Give Alexandra Williams a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.