Vue's Virtual DOM: what is it & how does it work?

Madeline CaplesMadeline Caples
6 min read

Introduction

Post Vue Conf 2025 (read about my post-conference reflections here!), I’m setting a goal of learning one (1) cool Vue thing per day. So far I’ve covered one cool Vue thing per week, but - hey! - we all have to start somewhere.

The reason I’m doing this is that it’s really easy to stagnate in your dev abilities. When you work on the same codebase(s) day after day, you find yourself reaching for the same patterns and solutions automatically. This can be beneficial (you get stuff done quickly), but it can also lead to boredom and burnout (scaffolding a Vue component again, no thank you!).

There are many solutions to the problem of dev boredom. Tech we can leverage to get the boring work done quicker, so we can focus on higher level problems, like coding agents, can be a life-saver when it comes to this. But today, I’m not going to talk about chatGPT, Cursor, or Copilot - today I’d like to suggest another cure for tech burnout, which is Learning Something New™.

With Learning Something New™ you have the opportunity to reflect on your acquired knowledge and boost it through further information. You can develop your skills as a developer, through deeper understanding of the technologies you use everyday. You can finally find out why your favorite technology works the way it does. And you can just plain old fill your brain with thoughts, which keeps your brain happy! Please join me in Learning Something New™ today, by reading about Vue’s Virtual DOM.

I discussed the Virtual DOM in this article, but today I’m going to try to go a bit deeper.

What is the Virtual DOM?

First thing’s first: what is the Virtual DOM? Here’s the definition I gleaned from reading Vue’s documentation: the Virtual DOM is an in-memory ideal representation of the User Interface (UI) that is used to sync up with the actual browser DOM, driving UI changes in response to data changes.

Let’s break that down. When data changes in the application, we want to reflect that to the user in the UI. What do I mean by “data changes”? It’s helpful to think of a user interaction, such as adding an item to a shopping cart. Once the user adds the item, you want your app to immediately show them that the item is in the cart. With the Virtual DOM we can keep these changes in memory and react to them in the actual DOM only as needed.

Why does it matter that the Virtual DOM is kept in memory? Well, it’s faster than grabbing the actual DOM from the page and loading it into memory for an operation, since it’s already there. We already have a record of the ideal state of the app that we can draw on and compare against the actual DOM.

Maybe you’re thinking, that’s really cool, Madeline, but a bit abstract. What is the Virtual DOM made of? How does it work? Well, the Virtual DOM is comprised of vnodes, which are objects that represent HTML elements (such as a <div>, <h1>, <section>, etcetera).

Here’s an example vnode, taken from Vue’s docs:

const vnode = {
  type: 'div',
  props: {
    id: 'hello'
  },
  children: [
    /* more vnodes */
  ]
}

We can see that the vnode is simply a Javascript object. It’s type is the kind of HTML element it represents, and it contains important data about the element in the object’s props. Everything we need to create an actual HTML element in the DOM is contained in this object. What are the children? Those might be filled in with even more vnodes, making this <div> the root of a Virtual DOM tree.

Before moving on to more nitty gritties, I’d like to address why we should even care about the Virtual DOM. The benefit of the Virtual DOM is that it abstracts the direct DOM manipulation away from the developer. This allows the developer to focus on creating their desired UI structures declaratively, rather than getting lost in a flurry of document.querySelectors. In addition, since the representation of the Virtual DOM is kept in memory, and can be checked against the actual DOM, minimal changes to the actual DOM may be required in the end, which means an improvement in performance.

So how does Vue’s Virtual DOM work under the hood? Let’s take a look at render pipelines.

What are Render Pipelines ?

Once you the developer write a template in your Vue file, what happens to it so that the browser can understand what to render? It needs to be processed by the render pipeline. There are three main steps to this pipeline: compile, mount, and patch. Here’s what each step does:

  1. Compile step: First the template is compiled into a render function that returns a virtual DOM tree (more on render functions below) - this tells us what structure the actual DOM should have.

  2. Mount step: The runtime renderer executes the render functions, walking the virtual DOM tree, and creates real DOM nodes according to its structure. This is a reactivity at work. A reactive effect is the mechanism that causes the UI to change in response to a change in data.

  3. Patch (a.k.a. diffing/reconciliation) step: This step is where Vue updates the actual DOM based on changes detected in the Virtual DOM. Let’s say a dependency used during mount changes - an updated virtual DOM tree is created in response. That in turn triggers a re-render: the runtime renderer walks the new virtual DOM tree and updates the actual DOM as needed.

Here’s a helpful graphic demonstrating this system, borrowed from the Vue docs:

Rendering mechanism chart from vue docs

We start out with our template in our Vue source code. The template gets compiled into render function code that depends on the component’s reactive state. The render function then returns a Virtual DOM tree which is mounted (in the case of starting the application) or patched (in the case of updating the application) into the actual DOM.

What is a Render Function?

Vue’s virtual DOM system is built on render functions. So what are they? As mentioned briefly above, a render function is a function which returns a virtual DOM tree, telling what structure the actual DOM should have once it is built by the runtime renderer. The function constructs vnodes, which you’ll recall are just Javascript objects that represent HTML elements.

Fun fact: you can actually code your own render functions, bypassing the compilation step from template into render function, if you wish. There are a few reasons why you may want to do this (besides that it’s just cool). As the Vue docs observe, “Render functions are more flexible than templates when dealing with highly dynamic logic, because you can work with vnodes using the full power of JavaScript.”

A small word of caution, however: While pure render functions are intrinsically necessary to Vue’s reactivity system, since templates are closer to HTML and generally simpler for people to reason about, it’s generally recommended that you start with templates, and only move on to creating your own render functions when a significant benefit presents itself.

Conclusion

Thank you for joining me today in Learning Something New™. I hope you indeed learned something, or at any rate, that your brain feels a little less itchy.

What cool Vue thing should we tackle next? Help me decide by placing your suggestions in the comments. I value your feedback, and thanks as always for reading!

Resources

1
Subscribe to my newsletter

Read articles from Madeline Caples directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Madeline Caples
Madeline Caples

I'm a frontend developer at Fuego Leads where I build cool stuff using Vue. I've worked there since April 2023. On Hashnode, I like to write about machine learning and other software engineering topics.