Here’s a concrete, simple example of what a Fiber node “looks like” and how it links to others, using a tiny DOM tree.
Example JSX
function App() {
return (
<div id="root-div" className="box">
<span>Hello</span>
<button>Click</button>
</div>
);
}
Conceptual Fiber nodes
- React creates one Fiber per element/component. Each Fiber points to:
- return: parent Fiber
- child: first child Fiber
- sibling: next child at the same level
- stateNode: the real DOM node (for host elements like div/span/button) or instance (for class components)
- alternate: the other version of this fiber (current vs work-in-progress) for double buffering
A rough, simplified shape (not exact internal names), for the initial mount:
Root fiber (HostRoot)
HostRootFiber = {
tag: 'HostRoot',
type: null,
key: null,
stateNode: rootContainer, // e.g., the DOM container from ReactDOM.createRoot(...)
pendingProps: null,
memoizedProps: null,
memoizedState: null,
child: DivFiber,
sibling: null,
return: null,
alternate: HostRootFiberWIP, // work-in-progress version
lanes: 0,
flags: 0
}
div fiber (the
)DivFiber = {
tag: 'HostComponent',
type: 'div',
key: null,
pendingProps: { id: 'root-div', className: 'box', children: [...] },
memoizedProps: { id: 'root-div', className: 'box', children: [...] }, // after commit
stateNode: HTMLDivElement, // real DOM node for this div
child: SpanFiber, // first child
sibling: ButtonFiber, // next child at same level
return: HostRootFiber, // parent
alternate: DivFiberWIP,
lanes: 0,
flags: Placement | Update? // flags for what to do in commit
}
span fiber (Hello)
SpanFiber = {
tag: 'HostComponent',
type: 'span',
key: null,
pendingProps: { children: 'Hello' },
memoizedProps: { children: 'Hello' },
stateNode: HTMLSpanElement,
child: TextFiber, // text nodes also get fibers
sibling: ButtonFiber,
return: DivFiber,
alternate: SpanFiberWIP,
lanes: 0,
flags: 0
}
text fiber ("Hello")
TextFiber = {
tag: 'HostText',
type: null,
key: null,
pendingProps: 'Hello',
memoizedProps: 'Hello',
stateNode: TextNode, // real DOM Text node
child: null,
sibling: null,
return: SpanFiber,
alternate: TextFiberWIP,
lanes: 0,
flags: 0
}
button fiber (Click)
ButtonFiber = {
tag: 'HostComponent',
type: 'button',
key: null,
pendingProps: { children: 'Click' },
memoizedProps: { children: 'Click' },
stateNode: HTMLButtonElement,
child: ButtonTextFiber,
sibling: null,
return: DivFiber,
alternate: ButtonFiberWIP,
lanes: 0,
flags: 0
}
How child/sibling/return link the tree
- HostRootFiber.child → DivFiber
- DivFiber.child → SpanFiber
- SpanFiber.sibling → ButtonFiber
- Each child’s return → DivFiber This creates a linked structure that React can traverse depth-first.
What happens on an update (e.g., className changes)
// Old: <div id="root-div" className="box">
function App() {
return (
<div id="root-div" className="box large">
<span>Hello</span>
<button>Click</button>
</div>
);
}
- Render (reconciliation) phase:
- React builds a work-in-progress (WIP) fiber tree using the old fibers via alternate pointers.
- For the div:
- Same type ('div') → update in place.
- pendingProps becomes { id: 'root-div', className: 'box large', ... }.
- React marks the fiber with an Update flag because props changed.
- Children (span/button) are same type and keys → reused; no remount.
- Commit phase:
- React reads flags and applies minimal DOM changes:
- For DivFiber, it updates the DOM node’s className from "box" to "box large".
- memoizedProps on the DivFiber become the new props.
- The WIP tree becomes current by swapping alternate pointers.
Keys in lists (quick note)
- If we had children like items.map(item => {item.name}), the key sits on each child fiber.
- During reconciliation, keys let React match old and new children in O(n) and decide which fibers to move, update, or delete without losing state.
Why this structure matters
- Pointers (child/sibling/return) let React pause/resume work and traverse efficiently.
- The alternate pointer enables double buffering: prepare updates off-screen, then commit atomically.
- Flags tell the commit phase exactly what DOM mutations to perform (Placement, Update, Deletion), minimizing work.
If you want, I can expand this with a component fiber (function component) example showing where hooks state lives and how updates enqueue on a fiber.
Top comments (0)