# Virtual DOM vs React DOM: Key Differences in React's Architecture

> Understand the key differences between React DOM and the virtual DOM. Learn how React's in-memory representation translates to real browser DOM changes for efficient UI updates.

- Repository: [Meta/react](https://github.com/facebook/react)
- Tags: deep-dive
- Published: 2026-02-20

---

**The virtual DOM is an in-memory JavaScript representation of the UI managed by React's reconciler, while React DOM is the concrete renderer that translates those changes into real browser DOM mutations.**

When discussing the virtual DOM vs React DOM in the `facebook/react` codebase, it is essential to understand that these are two distinct architectural layers. The virtual DOM handles the abstract representation and diffing logic, while React DOM serves as the web-specific host renderer that applies updates to the actual browser document.

## What Is the Virtual DOM?

The **virtual DOM** is an in-memory JavaScript representation of the UI, built from **Fiber** objects. It exists purely in memory and never touches the browser directly.

Implemented in the **React Reconciler** package (`packages/react-reconciler`), the virtual DOM constructs a tree of Fiber nodes from JSX and React elements. Its primary responsibilities include building this tree, performing the reconciliation algorithm to compute minimal changes, and scheduling work across synchronous, asynchronous, and concurrent modes.

The core work loop that manages this process lives in [`packages/react-reconciler/src/ReactFiberWorkLoop.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberWorkLoop.js). According to the `facebook/react` source code, the `performConcurrentWorkOnRoot` function drives the reconciliation process:

```javascript
// packages/react-reconciler/src/ReactFiberWorkLoop.js
export function performConcurrentWorkOnRoot(root, didTimeout) {
  // …reconcile fibers, generate side‑effects
}

```

Because it is pure JavaScript, the virtual DOM layer remains platform-independent. It can be used by any host environment including web, native, or server-side rendering contexts.

## What Is React DOM?

**React DOM** is the concrete renderer that knows how to translate virtual DOM changes into real browser DOM mutations. Unlike the platform-agnostic reconciler, React DOM is specific to the web host environment.

Implemented in the **React DOM** package (`packages/react-dom`), this layer receives the list of changes from the reconciler and maps React elements and Fibers to actual DOM nodes. It handles the application of updates, insertions, removals, and event listeners.

The public entry points reside in [`packages/react-dom/src/shared/ReactDOM.js`](https://github.com/facebook/react/blob/main/packages/react-dom/src/shared/ReactDOM.js), while modern root creation lives in [`packages/react-dom/src/client/ReactDOMClient.js`](https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOMClient.js). The `createRoot` function exemplifies how React DOM initiates the rendering process:

```javascript
// packages/react-dom/src/client/ReactDOMClient.js
export function createRoot(container) {
  // …create a root and schedule initial work
}

```

React DOM also exposes critical APIs like `ReactDOM.render`, `createRoot`, `hydrate`, and `flushSync` for synchronous updates.

## Key Differences Between Virtual DOM and React DOM

Understanding the distinction between these layers reveals React's modular architecture:

- **Platform Independence**: The virtual DOM is pure JavaScript and host-agnostic, usable by React Native, React Three Fiber, or custom renderers. React DOM is strictly the web implementation that interacts with `document` and `window` objects.

- **Responsibilities**: The virtual DOM builds Fiber trees and performs diffing algorithms. React DOM consumes the resulting side-effects and performs actual DOM mutations via `appendChild`, `setAttribute`, and similar APIs.

- **Location**: Virtual DOM logic resides in `packages/react-reconciler`, while React DOM code lives in `packages/react-dom`.

- **Extensibility**: The reconciler is generic; custom renderers implement their own host-config. React DOM represents the official web host-config, replaceable by other hosts like `react-native`.

## How They Work Together: The Rendering Flow

The interaction between virtual DOM and React DOM follows a precise pipeline:

1. **Entry Point**: `ReactDOM.render` or `createRoot` (from [`packages/react-dom/src/client/ReactDOMClient.js`](https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOMClient.js)) creates a root and hands the element tree to the reconciler.

2. **Reconciliation**: The reconciler builds the virtual DOM (Fiber tree) and runs the work loop in [`packages/react-reconciler/src/ReactFiberWorkLoop.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberWorkLoop.js).

3. **Diffing**: After comparing the new and previous virtual trees, the reconciler produces a list of side-effects (placements, updates, deletions).

4. **Commit Phase**: React DOM consumes these effects in [`packages/react-dom/src/shared/ReactDOM.js`](https://github.com/facebook/react/blob/main/packages/react-dom/src/shared/ReactDOM.js) through functions like `commitWork`, performing the actual browser DOM mutations.

5. **Flushing**: Updates are batched and flushed via the browser's event loop or `flushSync` from [`packages/react-dom/src/shared/ReactDOMFlushSync.js`](https://github.com/facebook/react/blob/main/packages/react-dom/src/shared/ReactDOMFlushSync.js) for synchronous guarantees.

This separation allows the heavy diffing logic to remain independent from host-specific mutation code, enabling React's declarative UI model across multiple platforms.

## Code Example: From JSX to Browser DOM

Consider a standard React application:

```jsx
// index.js
import React from 'react';
import {createRoot} from 'react-dom/client';

function Counter() {
  const [count, setCount] = React.useState(0);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Clicked {count} times
    </button>
  );
}

const root = createRoot(document.getElementById('root'));
root.render(<Counter />);

```

Underlying execution flow:

- `createRoot` instantiates a **root Fiber** in [`packages/react-dom/src/client/ReactDOMRoot.js`](https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOMRoot.js).
- `root.render` passes the JSX-generated React elements to the reconciler.
- `performConcurrentWorkOnRoot` builds the virtual DOM and detects changes.
- React DOM's `commitWork` applies the mutations to the real `button` element.

## Summary

- The **virtual DOM** is an in-memory Fiber tree managed by the React Reconciler (`packages/react-reconciler`), handling diffing and scheduling without touching the browser.
- **React DOM** is the web-specific renderer (`packages/react-dom`) that translates virtual DOM changes into actual DOM mutations.
- The **reconciler** is platform-agnostic, while **React DOM** is the host-specific implementation for browsers.
- Key files include [`ReactFiberWorkLoop.js`](https://github.com/facebook/react/blob/main/ReactFiberWorkLoop.js) for virtual DOM work and [`ReactDOM.js`](https://github.com/facebook/react/blob/main/ReactDOM.js) / [`ReactDOMClient.js`](https://github.com/facebook/react/blob/main/ReactDOMClient.js) for DOM operations.

## Frequently Asked Questions

### Is the virtual DOM the same as React DOM?

No. The virtual DOM is an abstract, in-memory representation of the UI constructed from Fiber nodes, while React DOM is the concrete renderer that writes those changes to the browser's actual DOM. They exist in separate packages (`react-reconciler` versus `react-dom`) within the `facebook/react` repository.

### What package contains the virtual DOM implementation?

The virtual DOM implementation resides in `packages/react-reconciler`, specifically within files like [`ReactFiberWorkLoop.js`](https://github.com/facebook/react/blob/main/ReactFiberWorkLoop.js) and [`ReactInternalTypes.js`](https://github.com/facebook/react/blob/main/ReactInternalTypes.js). This package defines the Fiber architecture and reconciliation algorithm that powers React's diffing capabilities.

### How does React Native fit into this architecture?

React Native replaces React DOM with its own host renderer while using the same virtual DOM layer. The reconciler (`react-reconciler`) remains unchanged, but instead of `react-dom` mutating HTML elements, React Native's renderer translates changes into native platform UI components (iOS/Android native views).

### Where does the reconciliation algorithm run?

The reconciliation algorithm runs entirely within the virtual DOM layer inside [`packages/react-reconciler/src/ReactFiberWorkLoop.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberWorkLoop.js). The `performConcurrentWorkOnRoot` function orchestrates this process, determining what has changed before handing a list of effects to React DOM for actual DOM manipulation.