At a very high level, Virtual DOM is a partial or full copy of the browser DOM tree structure. It is created by the frameworks like React and kept in the memory for manipulations via app code.
As and when necessary, virtual DOM is used by the app code to update the browser DOM – the DOM that actually represents the user interface. Ok, if it all sounds Greek to you, read on for clarity.
You will notice developers referring to the Virtual DOM simply as VDOM. The concept was first used by Meta (formerly Facebook) in React framework, and for that reason, developers also refer to VDOM as React Virtual DOM.
Before getting into further details of how React creates and uses the Virtual DOM, let us briefly look at what the browser (real) DOM is, and how it works.
Document Object Model (DOM)
Any web page you see in the browser is built and formatted using HTML, CSS, and media files. When you open a web page, the browser engine translates the underlying HTML into a tree-like data structure called Document Object Model (DOM tree).
The created DOM tree contains information related to every aspect of the user interface rendered in the browser. The DOM tree is made up of nodes where each node can have multiple nodes nested underneath.
Look at a sample DOM tree below, note that this tree shows only a few nodes and not everything related to the HTML shown on the left side-
Notice that there are different types of nodes in the DOM tree, like Element Node, Text node, Attribute Node and so on. For example <html> is the node that represents an html tag, similarly <body>, <h1>, <title>, <head> and <a> are also nodes of type element and represent tags from the supplied html document.
Need for Virtual DOM in React
In an application, when you intend to update the user interface, you can either load a different URL from the server or refresh the page (server call) to load the changed HTML/data (latest app state). Both these events require the browser to create a new DOM from the newly served HTML document.
Note that when you add, delete or modify a node in the DOM tree, the browser needs to perform calculations and adjust the screen layout and content to sync the user interface with the app state. In highly complex, reactive, and interactive applications where you see a continuous stream of data and events, the DOM may require frequent updates.
Updating DOM (& repainting the browser screen) at a high frequency is a big problem and that is where Virtual DOM in React js comes to the rescue.
Let us see how…
How Does React Utilize Virtual DOM?
When the app state changes due to calling the render() method or at the call of the setState() function, react creates a new virtual DOM. React then compares the new DOM tree with the existing DOM tree (already in memory) by using a process called diffing. Why is this necessary? This helps react identify all the DOM nodes that should be updated due to the state change.
Also note that Virtual DOM creation, diffing process, and all the fun happens in the background, and there is no browser repaint done, and for that reason, it is a very fast and efficient process.
But the user sees the screen, which is rendered from the real DOM… and not from the Virtual DOM, and for that reason, updating the virtual DOM in the background should mean nothing to the user interface. Yes, indeed, we will see how changes reach the user interface, but before that, we need to address the below question.
Is it really necessary to repaint the screen (the real DOM) on every state change event?
The answer is no! There are multiple scenarios where reflecting state change immediately on the screen wouldn’t be necessary, viable, or make any sense.
React batches the app state changes before pushing those to the real DOM. The latest virtual DOM diffing gives React all the impacted nodes. The impacted nodes of the virtual DOM are then pushed to the real DOM by a highly efficient process called reconciliation.
This way the real DOM gets updated only when necessary, reducing the need for frequent repaints of the user interface.
Look at the below example-
At the initial page load, the virtual DOM and the real DOM contain the same information. The first state change occurs and the text in the Virtual DOM is updated to “blue” from “black” and the color of the boxes remains the same. Note that the app developer wants to change text and color together, and hence real DOM remains unchanged.
The next state change brings the new box color as well, and at this event, your app updates the virtual DOM further to change the color of boxes as well as reconciles it with the real DOM to update the user interface (both color and text changed together).
The above example shows you how to save unnecessary screen re-renders. Though the example is hypothetical and simple, real-world apps benefit a lot from this design pattern.
Let us talk about Twitter. Imagine adding a new tweet to the list of existing tweets, the browser will need to paint the new node for the new tweet and also need to repaint other tweets at different screen coordinates.
If there are thousands of tweets/second and your app keeps repainting the screen continuously then the browser will become sluggish and jittery. And moreover, is it really useful? We don’t think so!
You may also like to read: best react online editors
More About React and Virtual DOM
When you initially load the React app, a tree of React elements is generated in the memory. This is the tree that we are calling React Virtual DOM tree. After the initial load, as and when the app code makes a call to the render() method again, a new tree of React elements is created.
The biggest and most time-consuming task is to identify what has changed in the new tree vs the old tree. And to identify that, React implements an O(n) heuristic algorithm that assumes that different elements produce different trees and that developers pass key prop to give a hint around which child elements in the tree may remain stable across renders.
If the developer marks a child element to be stable for a state change, the algorithm wouldn’t need to compare that element and the comparison process would be much faster. We will cover more about this in a separate article.
The outcome of the change identification (reconciliation) is the list of DOM nodes that must be updated. Post the reconciliation, react-dom (ReactDOM) applies the changes to the DOM nodes. Note the two key functions – ReactDOM.render() and setState(), these two automatically perform reconciliation behind the scene.
To make react performance even better, react has now introduced fiber architecture. The fiber architecture allows for incremental rendering. Incremental rendering means that rendering tasks can be split into smaller tasks split across frames. This is to ensure even smoother UI performance and efficient reconciliation between virtual DOM and actual DOM. Fiber architecture also provides features to pause, abort, and prioritize rendering based on updates received.