# How React Native Manages DOM Nodes Without a Browser Engine

> Discover how React Native manages its UI without a browser DOM. Learn about its lightweight DOM-API shim and C++ ShadowNodes for native view hierarchies.

- Repository: [Meta/react-native](https://github.com/facebook/react-native)
- Tags: internals
- Published: 2026-02-25

---

**React Native does not use a browser DOM; instead, it implements a lightweight DOM-API shim that maps familiar web methods onto native view hierarchies backed by C++ ShadowNodes.**

React Native is often misunderstood as running a hidden browser engine, but the `facebook/react-native` repository reveals a fundamentally different architecture. While the framework exposes **React Native DOM nodes** through JavaScript APIs that resemble browser DOM objects, these are merely thin wrappers around the framework's native rendering layer. Understanding this distinction is crucial for debugging layout issues and optimizing performance in production applications.

## Why React Native Does Not Use the Browser DOM

Unlike React for the web, which manipulates actual browser DOM nodes, React Native targets native platform views directly. The framework bridges JavaScript logic to native UI components (Android Views or iOS UIViews) without instantiating HTML elements or CSSOM trees. This architecture eliminates the overhead of a browser engine while maintaining the developer experience of writing declarative UI code.

## The Two-Layer Architecture: Shadow Nodes and DOM API Shims

React Native manages **React Native DOM nodes** through a dual-layer system that separates the high-level JavaScript API from the low-level native implementation.

### Shadow Nodes (C++ Core)

At the foundation lies the **Shadow Node** system implemented in C++. Each visual component in your React Native application corresponds to a `ShadowNode` object defined in [`ReactCommon/react/renderer/core/ShadowNode.h`](https://github.com/facebook/react-native/blob/main/ReactCommon/react/renderer/core/ShadowNode.h). These nodes form a parallel tree structure that represents the layout and styling information before it reaches the native platform views.

Shadow nodes are organized into families that enable efficient cloning and update tracking. When a component re-renders, React Native clones the affected shadow nodes rather than mutating them, ensuring thread safety and enabling asynchronous layout calculations.

### Web API Wrapper (JavaScript)

The JavaScript layer exposes DOM-like objects through classes such as `ReactNativeDocument`, `ReactNativeElement`, and `ReadOnlyElement`. These classes implement familiar DOM interfaces including `getElementById`, `getBoundingClientRect`, and layout properties like `offsetWidth`.

When you call a method on one of these JavaScript objects, the request flows through the **`NativeDOM`** TurboModule specified in [`packages/react-native/src/private/webapis/dom/nodes/specs/NativeDOM.js`](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/specs/NativeDOM.js). This module marshals the call across the bridge to the C++ implementation in [`ReactCommon/react/renderer/dom/DOM.cpp`](https://github.com/facebook/react-native/blob/main/ReactCommon/react/renderer/dom/DOM.cpp), which operates directly on the shadow node tree.

## How React Native DOM Nodes Are Created and Managed

The lifecycle of **React Native DOM nodes** involves three distinct phases that bridge the JavaScript and native layers.

### Linking the Root Shadow Node

When your React Native application mounts, the framework creates a root shadow node and links it to a document object via `NativeDOM.linkRootNode`. This establishes the connection between the JavaScript `ReactNativeDocument` instance and the underlying C++ shadow tree, enabling subsequent DOM queries to traverse the native hierarchy.

### Wrapping with JavaScript DOM Objects

Each shadow node in the native tree gets wrapped by a corresponding JavaScript object. For standard view components, this is a `ReactNativeElement` instance defined in [`packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js`](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js). These wrappers cache references to their native counterparts and expose the DOM API surface while delegating actual work to the `NativeDOM` module.

### Lifecycle Management Through Shadow Node Families

React Native manages component updates through **shadow node families** implemented in [`ReactCommon/react/renderer/core/ShadowNode.cpp`](https://github.com/facebook/react-native/blob/main/ReactCommon/react/renderer/core/ShadowNode.cpp). When props or state change, the framework clones the affected shadow node and its ancestors, creating a new immutable tree. The JavaScript wrappers update their internal pointers to reference the new shadow nodes, ensuring that DOM queries always reflect the current native state without requiring full tree reconstruction.

## Practical Examples: Working with React Native DOM Nodes

The following examples demonstrate how to interact with **React Native DOM nodes** using the Web API shim.

### Querying Elements by ID

To retrieve a specific view by its `nativeID` prop, use the `getElementById` method on the document object:

```javascript
import {createReactNativeDocument} from 'react-native/src/private/webapis/dom/nodes/ReactNativeDocument';

// rootTag is the identifier of the native root view provided by React Native
const doc = createReactNativeDocument(rootTag);

// Find a view that was assigned nativeID="myButton" in JSX
const button = doc.getElementById('myButton');

if (button) {
  console.log('Found element:', button.nodeName);
  console.log('offsetTop:', button.offsetTop);
  console.log('getBoundingClientRect():', button.getBoundingClientRect());
}

```

The `getElementById` call delegates to `NativeDOM.getElementById`, which traverses the C++ shadow tree to locate the corresponding `ShadowNode`. Source: [[`ReactNativeDocument.js`](https://github.com/facebook/react-native/blob/main/ReactNativeDocument.js)](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/ReactNativeDocument.js), [[`NativeDOM.js`](https://github.com/facebook/react-native/blob/main/NativeDOM.js)](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/specs/NativeDOM.js).

### Measuring View Dimensions

To obtain precise layout measurements, use the `measure` method available on `ReactNativeElement` instances:

```javascript
button.measure((x, y, width, height, pageX, pageY) => {
  console.log(`Relative position: (${x}, ${y})`);
  console.log(`Dimensions: ${width}×${height}`);
  console.log(`Absolute position: (${pageX}, ${pageY})`);
});

```

This method invokes `NativeDOM.measure`, which queries the layout data stored in the C++ shadow node and returns the cached values. Source: [[`ReactNativeElement.js`](https://github.com/facebook/react-native/blob/main/ReactNativeElement.js)](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js).

### Accessing Layout Properties

For synchronous access to layout metrics, reference the DOM-style offset properties:

```javascript
console.log('offsetWidth:', button.offsetWidth);
console.log('offsetHeight:', button.offsetHeight);
console.log('offsetParent tag:', button.offsetParent?.nodeName);

```

Properties like `offsetWidth` and `offsetParent` trigger `NativeDOM.getOffset` calls that read from the C++ shadow node's layout cache. Source: [[`ReactNativeElement.js`](https://github.com/facebook/react-native/blob/main/ReactNativeElement.js)](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js).

## Key Source Files and Implementation Details

The **React Native DOM nodes** implementation spans both JavaScript and C++ layers across the `facebook/react-native` repository:

| Area | File Path | Role |
|------|-----------|------|
| **Shadow Node Core** | [`ReactCommon/react/renderer/core/ShadowNode.h`](https://github.com/facebook/react-native/blob/main/ReactCommon/react/renderer/core/ShadowNode.h) | C++ class that represents a view in the Fabric renderer's shadow tree. |
| **Shadow Node Implementation** | [`ReactCommon/react/renderer/core/ShadowNode.cpp`](https://github.com/facebook/react-native/blob/main/ReactCommon/react/renderer/core/ShadowNode.cpp) | Logic for cloning, family management, mounting, and lifecycle tracking. |
| **DOM TurboModule Spec** | [`packages/react-native/src/private/webapis/dom/nodes/specs/NativeDOM.js`](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/specs/NativeDOM.js) | TurboModule interface exposing DOM-like methods to JavaScript. |
| **Document Implementation** | [`packages/react-native/src/private/webapis/dom/nodes/ReactNativeDocument.js`](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/ReactNativeDocument.js) | Implements the `document` object, `getElementById`, and root node linking. |
| **Element Implementation** | [`packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js`](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js) | Provides DOM properties (`offsetHeight`, `getBoundingClientRect`) and measurement APIs. |
| **Read-Only Base Classes** | [`packages/react-native/src/private/webapis/dom/nodes/ReadOnlyNode.js`](https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/webapis/dom/nodes/ReadOnlyNode.js) | Shared logic for nodes exposing read-only DOM attributes. |
| **Type Definitions** | [`packages/react-native/types/public/ReactNativeTypes.d.ts`](https://github.com/facebook/react-native/blob/main/packages/react-native/types/public/ReactNativeTypes.d.ts) | TypeScript declarations for DOM-like types (`DOMRect`, `Document`). |
| **C++ DOM Bridge** | [`ReactCommon/react/renderer/dom/DOM.h`](https://github.com/facebook/react-native/blob/main/ReactCommon/react/renderer/dom/DOM.h) and [`DOM.cpp`](https://github.com/facebook/react-native/blob/main/DOM.cpp) | Native implementation of DOM queries and layout calculations. |

These files demonstrate how React Native creates a DOM-style API on top of its native shadow-node architecture, allowing developers to write familiar DOM code while the framework efficiently manages the underlying native UI.

## Summary

- **React Native does not use a browser DOM.** Instead, it implements a lightweight DOM-API shim that maps web-compatible methods onto native view hierarchies.
- **Shadow Nodes form the core architecture.** These C++ objects (`ShadowNode` in [`ReactCommon/react/renderer/core/ShadowNode.h`](https://github.com/facebook/react-native/blob/main/ReactCommon/react/renderer/core/ShadowNode.h)) represent the actual UI tree and handle layout calculations.
- **JavaScript wrappers provide familiar APIs.** Classes like `ReactNativeDocument` and `ReactNativeElement` expose standard DOM methods while delegating to the `NativeDOM` TurboModule.
- **Operations bridge through NativeDOM.** Method calls on JavaScript DOM objects traverse the React Native bridge to C++ implementations in [`ReactCommon/react/renderer/dom/DOM.cpp`](https://github.com/facebook/react-native/blob/main/ReactCommon/react/renderer/dom/DOM.cpp).
- **Immutable updates ensure consistency.** Shadow node families enable efficient cloning and lifecycle management, ensuring DOM queries always reflect the current native state.

## Frequently Asked Questions

### Does React Native have a real DOM?

No, React Native does not have a real browser DOM. While it exposes JavaScript objects that resemble DOM nodes—with methods like `getElementById` and properties like `offsetWidth`—these are merely wrappers around the framework's C++ **Shadow Nodes**. The actual UI rendering uses native platform views (Android Views or iOS UIViews), not HTML elements.

### What is the difference between Shadow Nodes and DOM nodes in React Native?

**Shadow Nodes** are C++ objects defined in [`ReactCommon/react/renderer/core/ShadowNode.h`](https://github.com/facebook/react-native/blob/main/ReactCommon/react/renderer/core/ShadowNode.h) that represent the UI tree in React Native's Fabric renderer. They handle layout calculations, styling, and native view management. **DOM nodes** refer to the JavaScript wrapper objects (`ReactNativeElement`, `ReactNativeDocument`) that expose a web-compatible API. When you call a method on a DOM node, it delegates to the corresponding Shadow Node through the `NativeDOM` TurboModule.

### How do I measure a view's dimensions in React Native using DOM APIs?

You can use the `measure` method available on `ReactNativeElement` instances after obtaining a reference via `document.getElementById()`. The method accepts a callback that receives the view's relative coordinates, dimensions, and absolute page position. This call delegates to `NativeDOM.measure`, which retrieves cached layout data from the underlying C++ shadow node without triggering a re-layout.

### Are React Native DOM nodes compatible with web browser code?

Partially, but with significant limitations. React Native's DOM shim implements common methods like `getElementById`, `getBoundingClientRect`, and offset properties. However, it does not support HTML-specific features such as `innerHTML`, `className` string manipulation, or standard DOM event bubbling. Code relying on these browser-specific APIs requires polyfills or platform-specific adaptations to function in React Native.