What is Virtual DOM?

👨‍💻 Frontend Developer 🟢 Almost Certain 🎚️ Medium
#React

Brief Answer

Virtual DOM is a programming concept where an ideal or “virtual” representation of a user interface is kept in memory and synced with the “real” DOM. This allows optimizing UI updates by minimizing expensive operations with the real DOM.

Main reasons for using Virtual DOM:

  • Improves performance with frequent UI updates
  • Simplifies working with user interface state
  • Enables declarative approach to development
  • Abstracts DOM work from developers

What is Virtual DOM

Virtual DOM is a lightweight copy of the real DOM that is stored in memory. It’s not the actual DOM structure, but a representation of the DOM tree in the form of JavaScript objects. Virtual DOM allows comparing the current state of the interface with the new one and performing only necessary updates to the real DOM.

Key Virtual DOM Characteristics

  1. Lightweight — JavaScript objects consume fewer resources than real DOM
  2. Independence — works independently of the browser
  3. Comparison — allows comparing different interface states
  4. Optimization — minimizes operations with real DOM
// Virtual DOM example in React
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Counter: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

Why Virtual DOM is Needed

Virtual DOM was created to solve key performance problems when working with real DOM:

1. Slow Operations with Real DOM

Working with real DOM is one of the slowest operations in the browser:

// Problem: direct manipulation with real DOM
const container = document.getElementById('container');
 
// Each change triggers a repaint
container.innerHTML = '<p>First paragraph</p>';
container.innerHTML += '<p>Second paragraph</p>';
container.innerHTML += '<p>Third paragraph</p>';
// This triggers 3 separate repaints!
// Solution: Virtual DOM optimizes updates
function App() {
  const [items, setItems] = useState(['First', 'Second', 'Third']);
  
  const addItem = () => {
    setItems([...items, `Item ${items.length + 1}`]);
  };
  
  return (
    <div>
      {items.map((item, index) => <p key={index}>{item}</p>)}
      <button onClick={addItem}>Add item</button>
    </div>
  );
}
// Virtual DOM performs only one update to the real DOM

2. Complexity of Update Management

Without Virtual DOM, it’s difficult to track which parts of the interface need to be updated:

// Without Virtual DOM - complex update management
let todos = [
  { id: 1, text: 'Buy bread', completed: false },
  { id: 2, text: 'Call doctor', completed: true }
];
 
function updateTodo(id, completed) {
  // Find element in array
  const todo = todos.find(t => t.id === id);
  todo.completed = completed;
  
  // Manually update DOM
  const todoElement = document.querySelector(`[data-id="${id}"]`);
  if (todoElement) {
    todoElement.classList.toggle('completed', completed);
  }
}
// With Virtual DOM - automatic update management
function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Buy bread', completed: false },
    { id: 2, text: 'Call doctor', completed: true }
  ]);
  
  const toggleTodo = (id) => {
    setTodos(todos.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  };
  
  return (
    <ul>
      {todos.map(todo => (
        <li 
          key={todo.id} 
          data-id={todo.id}
          className={todo.completed ? 'completed' : ''}
          onClick={() => toggleTodo(todo.id)}
        >
          {todo.text}
        </li>
      ))}
    </ul>
  );
}

3. Declarative Approach

Virtual DOM allows using a declarative approach to development:

// Imperative approach (without Virtual DOM)
function updateUI(user) {
  // Manually update each element
  document.getElementById('username').textContent = user.name;
  document.getElementById('email').textContent = user.email;
  document.getElementById('avatar').src = user.avatar;
  
  // Manually change styles
  if (user.isOnline) {
    document.getElementById('status').className = 'online';
    document.getElementById('status').textContent = 'Online';
  } else {
    document.getElementById('status').className = 'offline';
    document.getElementById('status').textContent = 'Offline';
  }
}
// Declarative approach (with Virtual DOM)
function UserProfile({ user }) {
  return (
    <div>
      <img id="avatar" src={user.avatar} alt={user.name} />
      <h2 id="username">{user.name}</h2>
      <p id="email">{user.email}</p>
      <span 
        id="status" 
        className={user.isOnline ? 'online' : 'offline'}
      >
        {user.isOnline ? 'Online' : 'Offline'}
      </span>
    </div>
  );
}

How Virtual DOM Works

The Virtual DOM process consists of several stages:

1. Creating Virtual DOM

During the first render, React creates a Virtual DOM tree:

// Source component
function App() {
  return (
    <div className="container">
      <h1>Header</h1>
      <p>Paragraph</p>
    </div>
  );
}
 
// Virtual DOM representation (simplified)
const virtualDOM = {
  type: 'div',
  props: { className: 'container' },
  children: [
    {
      type: 'h1',
      props: null,
      children: ['Header']
    },
    {
      type: 'p',
      props: null,
      children: ['Paragraph']
    }
  ]
};

2. Comparison (Reconciliation)

When state changes, React creates a new Virtual DOM tree and compares it with the previous one:

// Previous state
const prevVirtualDOM = {
  type: 'ul',
  props: null,
  children: [
    { type: 'li', props: { key: '1' }, children: ['Item 1'] },
    { type: 'li', props: { key: '2' }, children: ['Item 2'] }
  ]
};
 
// New state (after adding item)
const nextVirtualDOM = {
  type: 'ul',
  props: null,
  children: [
    { type: 'li', props: { key: '1' }, children: ['Item 1'] },
    { type: 'li', props: { key: '2' }, children: ['Item 2'] },
    { type: 'li', props: { key: '3' }, children: ['Item 3'] }
  ]
};
 
// Virtual DOM determines that only one element needs to be added

3. Patch Generation

Based on differences, Virtual DOM creates a minimal set of changes:

// Patch for updating real DOM
const patches = [
  {
    type: 'INSERT',
    target: 'ul',
    element: { type: 'li', content: 'Item 3' },
    position: 'end'
  }
];

4. Applying Changes

Virtual DOM applies patches to the real DOM in one batch:

// One batch update instead of multiple separate operations
requestAnimationFrame(() => {
  // Apply all changes in one operation
  document.querySelector('ul').appendChild(newLiElement);
});

Reconciliation Algorithm

React uses a reconciliation algorithm to optimize Virtual DOM tree comparison:

1. Diffing Algorithm

React uses heuristics for fast tree comparison:

// React compares elements by type
function App({ isLoggedIn }) {
  // If type changes, React removes and recreates
  return isLoggedIn 
    ? <div>Content for authorized users</div>    // type: div
    : <span>Content for guests</span>;           // type: span
}

2. Keys

Keys help React efficiently update lists:

// ❌ Without keys - inefficient
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <li>{todo.text}</li>  // React can't track elements
      ))}
    </ul>
  );
}
 
// ✅ With keys - efficient
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>  // React can track elements
      ))}
    </ul>
  );
}

3. Element Stability

React assumes elements with the same type and key are stable:

// React can reuse DOM elements
function App({ items }) {
  return (
    <div>
      {items.map(item => (
        <div key={item.id}>
          <input value={item.text} />
          <span>{item.text}</span>
        </div>
      ))}
    </div>
  );
}

Virtual DOM Benefits

1. Performance

Virtual DOM minimizes operations with real DOM:

// Without Virtual DOM - multiple operations
for (let i = 0; i < 1000; i++) {
  const element = document.createElement('div');
  element.textContent = `Item ${i}`;
  document.body.appendChild(element);  // 1000 repaints
}
 
// With Virtual DOM - one batch operation
function App() {
  const items = Array.from({ length: 1000 }, (_, i) => `Item ${i}`);
  return (
    <div>
      {items.map((item, index) => <div key={index}>{item}</div>)}
    </div>
  );
  // One repaint after rendering the entire list
}

2. Abstraction from DOM

Virtual DOM hides the complexity of working with real DOM:

// Developer works with declarative code
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Counter: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
  // Virtual DOM takes care of optimizing updates
}

3. Platform Independence

Virtual DOM allows using the same code on different platforms:

// The same component can work in browser and on mobile devices
function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}
 
// In browser - renders to HTML
// In React Native - renders to native components

Virtual DOM Drawbacks

1. Additional Memory

Virtual DOM requires additional memory to store the virtual tree:

// Large application with many components
function LargeApp() {
  return (
    <div>
      <Header />
      <Navigation />
      <MainContent />
      <Sidebar />
      <Footer />
      {/* Each component creates virtual nodes */}
    </div>
  );
}

2. Computational Costs

Comparing Virtual DOM trees requires computational resources:

// Complex components with many elements
function ComplexList({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>
          <h3>{item.title}</h3>
          <p>{item.description}</p>
          <ul>
            {item.tags.map(tag => (
              <li key={tag}>{tag}</li>
            ))}
          </ul>
        </li>
      ))}
    </ul>
  );
  // Comparing large trees can be costly
}

3. Not Always Necessary

For simple applications, Virtual DOM may be overkill:

// Simple application without frequent updates
function StaticPage() {
  return (
    <div>
      <h1>Static page</h1>
      <p>This content rarely changes</p>
    </div>
  );
  // Virtual DOM doesn't provide benefits here
}

Virtual DOM Alternatives

1. Reactive Frameworks

Some frameworks use different approaches:

// Vue 3 Composition API (reactivity without Virtual DOM)
import { ref, computed } from 'vue';
 
export default {
  setup() {
    const count = ref(0);
    const doubleCount = computed(() => count.value * 2);
    
    const increment = () => {
      count.value++;
    };
    
    return { count, doubleCount, increment };
  }
};

2. Manual Optimization

You can optimize DOM work manually:

// Using requestAnimationFrame for optimization
function optimizedUpdate(data) {
  requestAnimationFrame(() => {
    // Group DOM changes
    const fragment = document.createDocumentFragment();
    
    data.forEach(item => {
      const element = document.createElement('div');
      element.textContent = item.text;
      fragment.appendChild(element);
    });
    
    document.getElementById('container').appendChild(fragment);
  });
}

When to Use Virtual DOM

Virtual DOM is particularly useful in the following cases:

  1. Complex interactive interfaces — applications with frequent data updates
  2. Component-based applications — architecture with many reusable components
  3. Team development — simplifies collaborative UI work
  4. Dynamic data — applications with real-time updates
// Great for complex interfaces
function Dashboard({ realtimeData }) {
  const [users, setUsers] = useState([]);
  const [messages, setMessages] = useState([]);
  const [notifications, setNotifications] = useState([]);
  
  // Frequent data updates
  useEffect(() => {
    const interval = setInterval(() => {
      // Update data every second
      fetchData().then(data => {
        setUsers(data.users);
        setMessages(data.messages);
        setNotifications(data.notifications);
      });
    }, 1000);
    
    return () => clearInterval(interval);
  }, []);
  
  return (
    <div className="dashboard">
      <UserList users={users} />
      <MessagePanel messages={messages} />
      <NotificationPanel notifications={notifications} />
    </div>
  );
}

Summary

Virtual DOM is a powerful concept that solves key performance problems when working with real DOM:

Main benefits:

  • Improves performance with frequent updates
  • Simplifies declarative interface development
  • Abstracts DOM work from developers
  • Enables creating complex interactive applications

Key concepts:

  • Lightweight DOM representation in memory
  • Comparison algorithm for optimizing updates
  • Batch application of changes to real DOM
  • Using keys for efficient list operations

Best practices:

  • Use unique keys in lists
  • Avoid excessive component complexity
  • Optimize rendering when necessary
  • Understand when Virtual DOM isn’t needed

Virtual DOM has become a standard in modern frontend development and enables creating fast, maintainable, and scalable web applications.


Want more articles to prepare for interviews? Subscribe to EasyAdvice, bookmark the site, and improve yourself every day 💪