What Is the Difference Between React and React DOM? A Deep Dive into the Architecture

React is the core library that defines what your UI should look like using elements, components, and hooks, while React DOM is the platform-specific renderer that knows how to create and update actual browser DOM nodes.

The distinction between React and React DOM represents one of the most fundamental architectural decisions in the React ecosystem. While often used together in web applications, these two packages serve distinct purposes that enable React to work across multiple platforms. Understanding the difference between React and React DOM is essential for debugging, optimizing performance, and leveraging React's cross-platform capabilities.

React: The Platform-Agnostic Core

React serves as the abstract engine that manages component logic, state, and the reconciliation algorithm. It operates entirely in JavaScript without any knowledge of the browser environment.

In packages/react/index.js, the core API exports fundamental building blocks including createElement, Component, useState, and the ReactVersion constant (lines 10-74). These exports define what the UI should render but remain completely agnostic about where that rendering occurs.

Key Responsibilities of the Core

  • Element Creation: The createElement function produces plain JavaScript objects describing UI structure.
  • Component Logic: Class components and function components manage state and lifecycle through hooks like useState and useEffect.
  • Reconciliation: The diffing algorithm determines which parts of the tree have changed between renders.

React DOM: The Browser-Specific Renderer

React DOM bridges the abstract React tree to the concrete browser environment. It implements the platform-specific logic required to manipulate the actual Document Object Model.

The DOM renderer lives in packages/react-dom/index.js, which re-exports the shared ReactDOM implementation from src/shared/ReactDOM.js. This architecture separates the public API from the internal implementation details.

DOM-Specific Implementation Details

In packages/react-dom/src/shared/ReactDOM.js (lines 14-68), the renderer implements browser-only functionality:

  • createPortal: Allows rendering children into a DOM node outside the parent component hierarchy.
  • flushSync: Forces synchronous updates for testing or integration with non-React code.
  • Container Validation: Ensures target containers are actual DOM nodes using utilities from packages/react-dom-bindings/src/client/ReactDOMContainer.js.

How the Separation Enables Cross-Platform Development

The decoupling of React from React DOM allows the same component code to run on entirely different platforms. This is achieved through the reconciler pattern, where the core React engine coordinates with platform-specific renderers via a host configuration.

The reconciler itself lives in packages/react-reconciler/src/ReactFiberReconciler.js. It handles the diffing algorithm and component lifecycle logic, while individual renderers supply the host configuration that tells the reconciler how to create and mutate platform-specific nodes.

Alternative renderers include:

  • React Native: Renders to native iOS/Android UI components instead of DOM elements.
  • React Three Fiber: Renders to Three.js 3D scenes and WebGL objects.
  • React Test Renderer: Renders to pure JavaScript objects for snapshot testing without a browser.
  • React Server: Renders components to HTML strings on the server.

Practical Code Examples

Creating Elements with React Core

This example uses only the React package, with no DOM-specific code:

import {createElement, useState} from 'react';

// Pure React element – can be rendered by any renderer
function Counter() {
  const [count, setCount] = useState(0);
  return createElement('button', {onClick: () => setCount(c => c + 1)},
    `Count: ${count}`);
}

Rendering to the DOM with React DOM

This example bridges the abstract component to the browser:

import {createRoot} from 'react-dom/client';
import {createElement} from 'react';
import {Counter} from './Counter';

const container = document.getElementById('root');
const root = createRoot(container);
root.render(createElement(Counter));

Using an Alternative Renderer

This demonstrates how the same component works with React Test Renderer:

import TestRenderer from 'react-test-renderer';
import {createElement} from 'react';
import {Counter} from './Counter';

const testInstance = TestRenderer.create(createElement(Counter));
console.log(testInstance.toJSON());

Key Source Files and Their Roles

Understanding the difference between React and React DOM requires examining their source locations in the Facebook React repository:

Summary

  • React is the platform-agnostic core that defines UI structure and behavior through elements, components, and hooks, independent of any rendering environment.
  • React DOM is the browser-specific renderer that creates and manages actual DOM nodes, handling platform-specific concerns like events and portals.
  • The architectural separation allows React to work across multiple platforms (Native, Three.js, Server) using different renderers while sharing the same component code.
  • The reconciler (ReactFiberReconciler) bridges the core and platform-specific implementations, coordinating updates through host configurations.

Frequently Asked Questions

Can you use React without React DOM?

Yes. React can be used with alternative renderers such as React Native for mobile apps, React Three Fiber for 3D scenes, or React Test Renderer for unit testing. These packages replace React DOM while using the same React core API, allowing components to remain completely portable across platforms.

Why are React and React DOM separate packages?

The separation follows the renderer pattern, allowing the same component logic to run on different platforms. By keeping the core platform-agnostic, Meta can maintain one reconciliation engine while community and internal teams build specialized renderers for web, mobile, and other environments. This modularity reduces bundle sizes for non-DOM targets and enforces clean architectural boundaries.

What happens if I import createElement from react-dom instead of react?

You should not import createElement from react-dom because it is not exported there. Attempting to do so will result in an import error or undefined. Always import createElement, Component, and hooks from the react package, and import renderer-specific methods like createRoot or createPortal from react-dom.

Is the reconciler the same in React and React DOM?

The reconciler is shared between them. React DOM consumes the reconciler from react-reconciler to apply updates to the browser DOM. The reconciler handles the diffing algorithm and component lifecycle logic, while React DOM provides the host configuration that tells the reconciler how to create and mutate actual DOM nodes. This shared reconciler is what makes cross-platform rendering possible.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →