How React creates a virtual representation and diffs it to find minimal DOM changes.
The Virtual DOM is a lightweight JavaScript object tree that mirrors the real DOM. React keeps two copies: the current tree and the 'work-in-progress' tree built during each render. Instead of touching the real DOM immediately, React diffs the two virtual trees and computes the minimal set of changes needed β then applies them in a single batch.
When you write JSX, Babel (or the React compiler) transforms it into React.createElement(type, props, ...children) calls. These return plain JavaScript objects called React elements β not real DOM nodes.
React.createElement() returns a nested object tree like '{' type: 'div', props: '{' className: 'box', children: [...] '}' '}'. This is the Virtual DOM β a cheap in-memory description of what the UI should look like.
When state or props change, React calls your component function again and produces a brand-new Virtual DOM tree representing the new desired UI.
React's diffing algorithm (O(n) heuristic) walks both trees simultaneously. It assumes that elements of different types produce different trees, and uses the key prop to match list items across renders.
After diffing, React has a list of mutations: update this prop, insert this node, remove that node. These are applied to the real DOM in the commit phase β minimizing reflows and repaints.
A plain JS object returned by React.createElement(). Describes type, props, and children. Not a real DOM node.
React's O(n) heuristic for comparing two virtual trees. Assumes same-position elements are the same type unless keys differ.
The process of comparing old and new virtual trees and determining the minimal set of DOM operations needed.
A stable identity for list items. Lets React match old and new list elements even if they moved positions.
React collects all DOM mutations from a reconciliation pass and applies them together, reducing browser reflows.
1// JSX you write:2const element = <div className="box"><span>Hello</span></div>;34// Compiles to:5const element = React.createElement(6 'div',7 { className: 'box' },8 React.createElement('span', null, 'Hello')9);1011// Produces this Virtual DOM object:12{13 type: 'div',14 props: {15 className: 'box',16 children: {17 type: 'span',18 props: { children: 'Hello' }19 }20 }21}
Direct DOM manipulation is expensive because the browser must recalculate layout and repaint. By diffing in JavaScript (fast) and batching DOM writes (slow), React minimizes the number of expensive browser operations. This is why React apps feel fast even with complex UIs.
You have a chat app rendering 500+ messages. Scrolling to load older messages causes the entire message list to re-render, taking 300ms and causing visible lag.
When new messages are appended, React creates a new VDOM tree for the entire list. The diffing algorithm walks all 500+ nodes even though only 20 new messages were added.
Use windowing (react-window/react-virtualized) to only render visible messages in the VDOM. Combined with proper `key` props, React's diff only processes ~20 visible nodes instead of 500+. The VDOM tree stays small, diffs stay fast.
Takeaway: The VDOM's diffing cost is proportional to tree size, not DOM size. Keeping your VDOM tree small via virtualization is the most effective optimization for large lists.
A component receives `style={{ color: 'red' }}` as a prop. Despite no visual change, React DevTools shows it re-renders on every parent update.
Each render creates a NEW style object `{ color: 'red' }`. When React diffs oldProps vs newProps, `oldStyle !== newStyle` (different object reference), so React marks this node as 'changed' and commits a DOM update β even though the actual CSS hasn't changed.
Hoist the style object outside the component or wrap it in `useMemo`. Now `oldStyle === newStyle` (same reference), the diff correctly marks it as 'unchanged', and React skips the DOM write entirely.
Takeaway: Understanding how the VDOM diff compares props (by reference, not deep equality) reveals why inline objects, arrays, and arrow functions in JSX cause performance issues.