Skip to content

What is Fiber in React? Understanding stateNode and the Virtual DOM Blueprint

The Confusion

I kept hearing about “Fiber” in React. Blog posts mentioned it. Conference talks praised it. Documentation referenced it. But what actually IS it?

Is it a new rendering engine? A virtual DOM replacement? Some magical optimization layer?

Every explanation I found used terms like “reconciliation algorithm,” “work scheduling,” and “incremental rendering.” Technical, sure, but not helpful for building intuition.

Then I dug into React’s source code and found something surprising: Fiber is just a JavaScript object. A plain, boring object with properties like child, sibling, return, and stateNode.

The Simple Truth

Fiber = In-memory JavaScript object blueprint of your JSX

stateNode = Reference to the actual DOM node or component instance

That’s it. No magic. No mysterious black box. Just structured data that describes what your UI should look like and how it connects to the real browser DOM.

What is a Fiber Node?

When you write JSX like this:

App.jsx
function App() {
return (
<div id="container">
<h1>Hello</h1>
<p>World</p>
</div>
);
}

React transforms each element into a Fiber node. A simplified Fiber structure looks like this:

Fiber structure (simplified)
{
// Identity
tag: WorkTag, // Type: function component, class component, DOM element, etc.
key: null | string, // React key for reconciliation
type: any, // 'div', 'span', or component function/class
// Instance connection
stateNode: any, // <-- THE KEY PROPERTY: actual DOM node or instance
// Tree structure (linked list)
return: Fiber | null, // Parent fiber
child: Fiber | null, // First child fiber
sibling: Fiber | null, // Next sibling fiber
// State & Props
pendingProps: mixed, // New props from render
memoizedProps: mixed, // Props from previous render
memoizedState: mixed, // State from previous render
flags: Flags, // Side effects (update, delete, etc.)
}

Each Fiber represents one unit of work. The child, sibling, and return properties form a linked-list tree structure that React can traverse efficiently.

The Fiber Tree Visualized

Here’s how your JSX transforms into a Fiber tree:

JSX to Fiber tree transformation
JSX: Fiber Tree:
------------------------------ ---------------------------------
<App> AppFiber (FunctionComponent)
<div id="container"> |
<h1>Hello</h1> v
<p>World</p> divFiber (HostComponent)
</div> |
</App> v
h1Fiber ----> pFiber
| |
v v
"Hello" "World"

The return property points back to the parent (I drew arrows going down, but return goes back up).

What is stateNode?

The stateNode property is the critical bridge between React’s virtual world and the real browser DOM:

stateNode types by component type
Component Type stateNode Value
----------------- ----------------
<div>, <span>, etc. Actual DOM element (document.createElement result)
Class Component Class instance (this inside the component)
Function Component null (no instance exists)

For host components (DOM elements), stateNode holds the actual DOM node:

stateNode for DOM elements
const divFiber = {
tag: HostComponent, // 5 in React source
type: 'div',
stateNode: document.createElement('div'), // Real DOM node!
};

For class components, stateNode holds the class instance:

stateNode for class components
class Counter extends React.Component {
state = { count: 0 };
render() { return <div>{this.state.count}</div>; }
}
const classFiber = {
tag: ClassComponent, // 1 in React source
type: Counter,
stateNode: new Counter(props), // The 'this' inside methods
};

For function components, stateNode is null because functions don’t have instances:

stateNode for function components
function Counter() {
const [count, setCount] = useState(0);
return <div>{count}</div>;
}
const functionFiber = {
tag: FunctionComponent, // 0 in React source
type: Counter,
stateNode: null, // No instance!
};

Why stateNode Matters

This is how React actually updates the real DOM. When the commit phase runs:

Conceptual DOM update via stateNode
function commitUpdate(fiber) {
const domNode = fiber.stateNode; // Get real DOM node
const newProps = fiber.memoizedProps;
// Direct DOM manipulation
if (newProps.className !== fiber.pendingProps.className) {
domNode.className = newProps.className;
}
if (newProps.style) {
Object.assign(domNode.style, newProps.style);
}
// ... handle other props
}

React doesn’t magically update the DOM. It uses stateNode to find and modify actual DOM elements.

Fiber Tree vs Virtual DOM

Here’s what confused me initially: they’re the same thing.

Terminology mapping
Concept Name Implementation Name
----------------- -------------------
Virtual DOM = Fiber Tree (React 16+)
Virtual DOM Node = Fiber Node
Reconciliation = Fiber Reconciler

Before React 16, React used a “stack reconciler” that recursively processed updates. This was blocking - once rendering started, it couldn’t stop until finished.

React 16 introduced the “Fiber reconciler” with interruptible rendering:

Stack vs Fiber reconciler
Stack Reconciler (pre-React 16):
render() ----[blocking]----> commit()
(jank, frozen UI during render)
Fiber Reconciler (React 16+):
render() --[chunk]--[chunk]--[chunk]--> commit()
(browser can handle user input between chunks)

This enables React 18’s concurrent features: transitions, suspense, and priority-based updates.

memoizedState vs stateNode

Another source of confusion: these two properties store different things.

memoizedState vs stateNode
Property What it stores
-------------- ----------------
stateNode DOM node OR class instance
memoizedState Hook state for function components
OR component state for class components

For function components with hooks, memoizedState stores a linked list of hook states:

Hook state in memoizedState
function Counter() {
const [count, setCount] = useState(0); // Hook 1
const [name, setName] = useState(''); // Hook 2
// fiber.memoizedState structure:
// { memoizedState: 0, next: { memoizedState: '', next: null } }
}

This is why hooks must be called in the same order - React relies on the linked list structure to find each hook’s state.

Tracing a Real Component

Let me trace how a simple component becomes Fiber nodes:

SimpleComponent.jsx
function SimpleComponent() {
const [value, setValue] = useState('hello');
return (
<div className="container">
<span>{value}</span>
</div>
);
}

This creates the following Fiber tree:

Generated Fiber nodes
// Function component fiber
const simpleFiber = {
tag: FunctionComponent,
type: SimpleComponent,
stateNode: null,
memoizedState: { memoizedState: 'hello', next: null }, // useState hook
child: divFiber,
};
// Div fiber
const divFiber = {
tag: HostComponent,
type: 'div',
stateNode: document.createElement('div'), // Real DOM node
pendingProps: { className: 'container' },
child: spanFiber,
return: simpleFiber,
};
// Span fiber
const spanFiber = {
tag: HostComponent,
type: 'span',
stateNode: document.createElement('span'),
child: textFiber,
return: divFiber,
};
// Text fiber
const textFiber = {
tag: HostText,
stateNode: { nodeValue: 'hello' }, // Text content
return: spanFiber,
};

How React Traverses the Fiber Tree

React uses a depth-first traversal pattern:

Fiber tree traversal (conceptual)
function traverseFiber(fiber) {
let node = fiber;
while (node) {
// 1. Process current node
processFiber(node);
// 2. Go to child first (depth-first)
if (node.child) {
node = node.child;
}
// 3. No child? Try sibling
else if (node.sibling) {
node = node.sibling;
}
// 4. No child or sibling? Go back up to parent's sibling
else {
while (node && !node.sibling) {
node = node.return; // Go back up
}
if (node) {
node = node.sibling;
}
}
}
}

This traversal happens during both the render phase (building the new tree) and commit phase (applying changes to DOM).

Key Takeaways

  1. Fiber is just a data structure - A JavaScript object with tree links (child, sibling, return) and state references (memoizedState, stateNode)

  2. stateNode is the DOM bridge - For host components, it’s the real DOM element. For class components, it’s the instance. For function components, it’s null.

  3. Virtual DOM = Fiber Tree - Different names for the same concept. “Virtual DOM” is the abstraction; “Fiber” is React’s implementation.

  4. Understanding this helps debugging - When React DevTools shows you a component tree, you’re looking at Fiber nodes. When performance suffers, understanding the Fiber tree helps identify bottlenecks.

  5. Hooks rely on memoizedState order - The linked list of hook states explains why you can’t call hooks conditionally.

When This Knowledge Helps

  • Debugging React DevTools Fiber tree view
  • Understanding why concurrent mode improves performance
  • Optimizing re-renders by understanding memoization
  • Building custom React renderers (React Native, React Three Fiber)
  • Debugging hydration mismatches in SSR

The next time someone talks about “Fiber architecture” or asks about React internals, you can confidently say: “It’s just a tree of JavaScript objects that describe your UI, with stateNode pointing to the real DOM.”

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments