Skip to content

Fix flex shrink sizes when the first pass freezes items - #2044

Open
intergalacticspacehighway wants to merge 1 commit into
react:mainfrom
intergalacticspacehighway:fix-flex-shrink-frozen-total
Open

intergalacticspacehighway wants to merge 1 commit into
react:mainfrom
intergalacticspacehighway:fix-flex-shrink-frozen-total

Conversation

@intergalacticspacehighway

@intergalacticspacehighway intergalacticspacehighway commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2038
Fixes #1651
Fixes #931
Closes #2039
Closes #1855

Thanks to @sebholl for the analysis and the reproduction in #2038.

Issue

When a flex line overflows, the first pass freezes the items that hit their minimum size and removes them from the shrink total. The second pass then divides the remaining free space by that total.

The total was computed by subtracting the frozen items flex shrink factors from the float total. When every item freezes, the total should become 0, but float subtraction leaves a small residual.

Example

All items have flexShrink: 1:

row (width: 350, flexDirection: row)
├── A  width: 100.1  minWidth: 90
├── B  width: 200.3  minWidth: 190
└── C  width: 300.7  minWidth: 290

The items need 601.1 but the row is only 350, so they must shrink by 251.1. Each item would go below its minWidth, so the first pass freezes all three (at 90 / 190 / 290) and removes them from the shrink total. With no items left, the total should be 0. But in float, -601.1 + 100.1 + 200.3 + 300.7 = 0.000061.

That is above the 1e-6 guard added in #1974 (for #1665), so the second pass divides the remaining free space by it: -220 / 0.000061 ≈ -3.6 million. Instead of staying at their min, the items grow to about 361M / 722M / 1084M (expected 90 / 190 / 290).

Solution

Instead of subtracting the frozen item's flex shrink factors, sum the factors of the unfrozen items and use that. This removes the float residue when subtracting values. The sum would be exactly 0 if all the items are frozen. Because the total is now exact, the second pass checks == 0 instead of the 1e-6 guard from #1974. In that case it uses childSize = 0, so the clamp sets each item to its min. This also fixes #931, where basis * (1 - flexShrink) stayed above the min when flexShrink < 1.

The second pass divided the remaining free space by the shrink total minus the
frozen items' factors, and in float that subtraction leaves a residual (react#2038,
react#1651). Sum the factors of the items that are not frozen instead, which is
exactly 0 when none remain, and then send items to their lower bound (react#931).
@intergalacticspacehighway
intergalacticspacehighway marked this pull request as ready for review October 18, 2026 18:46
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 18, 2026
@meta-codesync

meta-codesync Bot commented Oct 19, 2026

Copy link
Copy Markdown

@cortinico has imported this pull request. If you are a Meta employee, you can view this in D126038629.

@intergalacticspacehighway

Copy link
Copy Markdown
Collaborator Author

@cortinico Thanks for importing. Facebook Internal builds & tests seems to be failing. Could you share which tests fail?

@cortinico

Copy link
Copy Markdown
Contributor

Facebook Internal builds & tests seems to be failing. Could you share which tests fail?

Those were noisy signals. It's queued for landing now @intergalacticspacehighway 👍

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

2 participants