In the previous chapter, Yoga Node (The Layout Block), we learned how to build a tree of nodes and give them style rules. But a tree of rules is just a wish list. To draw pixels on the screen, we need concrete numbers.
This chapter explores the Recursive Layout Algorithm. This is the engine that runs when you call .calculateLayout(). It turns your "wishes" (styles) into reality (coordinates).
Layout is difficult because it is a negotiation.
Imagine a parent box that is 200px wide. It has two children:
100px wide.flex-grow (take up remaining space).The parent cannot calculate Child B's width until it knows Child A's width. And Child A cannot know its final position until the Parent knows its own size.
To solve this, the engine uses Recursion.
Before we look at the algorithm, we must understand the vocabulary parents use to talk to children. When a parent node asks a child node to measure itself, it uses a Measure Mode.
Think of a parent pouring water into a container (the child):
MeasureMode.Exactly)width: 100. The engine tells the node: "You are 100. Deal with it."MeasureMode.AtMost)200px wide column. The text can be narrow, but it cannot exceed 200px.MeasureMode.Undefined)
When you trigger calculateLayout, a wave of calculations travels down the tree (input) and bubbles back up (output).
Here is the conversation that happens for a simple Button with Text:
The heart of native-ts is a single, massive function called layoutNode. It is located in yoga-layout/index.ts.
It handles the complexity of Flexbox in one giant pass (mostly).
The function signature tells the whole story. It takes a node and the constraints from the parent.
// yoga-layout/index.ts
function layoutNode(
node: Node,
availableWidth: number, // How much space parent has
availableHeight: number,
widthMode: MeasureMode, // The "Vocabulary" (Exactly, AtMost...)
heightMode: MeasureMode,
// ... other context
): void {
// ...
}
Flexbox is directional. If flexDirection is Row:
The algorithm calculates the Main Axis first because that determines how much space is left over.
// Inside layoutNode...
// 1. Determine direction
const mainAxis = node.style.flexDirection;
const isRow = mainAxis === FlexDirection.Row;
// 2. Determine available size for main axis
const mainSize = isRow ? availableWidth : availableHeight;
The engine loops through all children to ask: "What is your Flex Basis?" (How big do you start?).
// Simplified logic from the engine
let totalFlexBasis = 0;
for (const child of node.children) {
// Recursively ask child how big it wants to be
child.resolveFlexBasis(availableWidth, ...);
// Add it to the total tally
totalFlexBasis += child.getFlexBasis();
}
This is the magic of Flexbox. If totalFlexBasis is less than the parent's size, we have empty space. The engine checks flexGrow to see who gets that space.
const remainingSpace = availableWidth - totalFlexBasis;
if (remainingSpace > 0) {
// Distribute extra space to children with flexGrow > 0
distributeFreeSpace(children, remainingSpace);
}
Once the Main Axis (Width) is locked in, the engine looks at the Cross Axis (Height).
If a child is set to alignSelf: 'stretch', this is where the parent forces the child to expand to match the parent's height.
for (const child of node.children) {
if (child.style.alignSelf === Align.Stretch) {
// Force child height to match parent height
child.layout.height = node.layout.height;
}
}
Finally, now that every child has a concrete Width and Height, the engine acts like a coordinate calculator.
It applies justifyContent (Main Axis alignment) and alignItems (Cross Axis alignment) to set node.layout.left and node.layout.top.
// Example: justifyContent = 'center'
let currentX = (availableWidth - totalContentWidth) / 2;
for (const child of node.children) {
child.layout.left = currentX;
currentX += child.layout.width; // Move cursor for next child
}
You don't call layoutNode manually. You call calculateLayout on the root, and the recursion handles the rest.
Imagine a box that needs to adapt.
const root = new Node();
root.setWidth(500); // Parent is 500px wide
root.setFlexDirection(FlexDirection.Row);
const childA = new Node();
childA.setFlexGrow(1); // "I want all the space I can get"
const childB = new Node();
childB.setWidth(100); // "I want exactly 100px"
root.insertChild(childA, 0);
root.insertChild(childB, 1);
// TRIGGER THE ALGORITHM
root.calculateLayout(500, 300);
What happens recursively?
flexGrow: 1. It gets all 400px.The Recursive Layout Algorithm is the brain of the project.
However, doing this math for every single pixel change is slow. In the next chapter, we will learn how the engine remembers its homework so it doesn't have to recalculate everything every time.
Next Chapter: Layout Caching System
Generated by Code IQ