Reference: Planning-nl/flexbox.js
The algorithm works in multiple passes:
- Setup: Initialize axis sizes, reset shrunk flag
- Layout Main Axis: Distribute items, apply grow/shrink
- Layout Cross Axis: Apply alignment
Flexily provides two layout algorithm implementations:
| Algorithm | File | Default | Strengths |
|---|---|---|---|
| Zero-alloc | layout-zero.ts |
✅ Yes | Faster for flat layouts, no GC pressure |
| Classic | classic/layout.ts |
No | Simpler code, good for debugging |
Both implement identical Yoga-compatible behavior. The zero-alloc version uses pre-allocated arrays and node-attached FlexInfo structs to eliminate temporary allocations during layout.
Input: amount (extra space to distribute)
1. Calculate totalGrowAmount = sum of all flex-grow values for items not at max size
2. If no grow items, exit
3. Loop while amount remaining:
a. amountPerGrow = remaining / totalGrowAmount
b. For each item:
- If item.grow > 0:
- grow = item.grow * amountPerGrow
- If item has maxSize and size >= maxSize: skip (fully grown)
- If item has maxSize and size + grow > maxSize:
- grow = maxSize - size (cap at max)
- totalGrowAmount -= item.grow (remove from next iteration)
- finalSize = size + grow
- Resize item to finalSize
- remaining -= grow
- If remaining ~= 0: done
Key insight: The algorithm iterates multiple times to handle items that hit their max size.
Input: amount (space to remove)
1. Calculate totalScaledShrink = sum of (flex-shrink × flex-basis) for items above min size
2. If no shrinkable items, exit
3. Loop while amount remaining:
a. For each item:
- If item.shrink > 0 and size > minSize:
- scaledShrink = item.shrink * item.baseSize
- shrink = remaining * scaledShrink / totalScaledShrink
- maxShrink = size - minSize
- If shrink >= maxShrink:
- shrink = maxShrink (cap at min)
- totalScaledShrink -= scaledShrink (remove from next iteration)
- finalSize = size - shrink
- Resize item to finalSize
- remaining -= shrink
- If remaining ~= 0: done
Key insight: Shrink is proportional to flex-shrink × flex-basis (CSS spec compliant).
Items with larger basis values shrink more, matching browser behavior.
For each flex line, layoutNode():
- Resolves flex sizes —
distributeFlexSpaceForLine()grows items when free space is positive and shrinks them when negative (the two algorithms above) - Positions items along the main axis — applies
justifyContentspacing - Measures the cross axis — the line's cross size is the max of its items'
cross sizes;
alignItems/alignSelfthen place each item within the line
Main-axis alignment supports:
- flex-start (default): items at start
- flex-end: items at end
- center: items centered
- space-between: first/last at edges, rest evenly spaced
- space-around: equal space around each item
- space-evenly: equal space between and around items
Both algorithms fully support RTL (right-to-left) layouts:
DIRECTION_RTLreverses the main axis for row layoutsEDGE_START/EDGE_ENDresolve to physical edges based on direction- All margin, padding, and position edges respect direction
The resolveEdgeValue() function maps logical edges to physical edges:
- In LTR: START → LEFT, END → RIGHT
- In RTL: START → RIGHT, END → LEFT
Baseline alignment is supported via setBaselineFunc():
node.setBaselineFunc((width, height) => {
// Return the baseline offset from the top
return height * 0.8 // Example: 80% down
})The baseline is used when ALIGN_BASELINE is set on the container's alignItems.
- No order property: Items laid out in insertion order
- No writing modes: Horizontal-tb only
| File | Description |
|---|---|
src/layout-zero.ts |
Layout algorithm (default, ~2700 lines) |
src/node-zero.ts |
Node class with FlexInfo |
src/index.ts |
Default export |
src/classic/layout.ts |
Classic layout algorithm (~1800 lines) |
src/classic/node.ts |
Classic Node class |
src/index-classic.ts |
Classic export |