`flex` emits a percentage flex-basis, making layout very slow on Safari 27 / WebKit
Is there an existing issue for this?
- I have searched the existing issues
Describe the issue
We started receiving support reports about our web app hanging or freezing on Safari 27. After investigating, the issue appears to be related to how WebKit handles percentage values for flex-basis.
Since our app is built with react-native-web, it makes extensive use of flex: 1, which generates the following CSS rule:
.r-flex-13awgt0 { flex: 1 1 0%; }
⬇️ The technical details below are AI-generated, as the relevant WebKit internals go well beyond what I can confidently reason about. ⬇️
A percentage flex-basis has to be resolved against the flex container's inner main size, so WebKit walks up the ancestor chain for every flex item on every layout. In WebKit that walk is itself recursive — RenderFlexibleBox::canUseFlexItemForPercentageResolution → canComputePercentageFlexBasis → computePercentageLogicalHeightGeneric → RenderBlock::availableLogicalHeightForPercentageComputation → back to canUseFlexItemForPercentageResolution. Because flex: 1 is the single most common style in any react-native-web app, essentially every nested View is on that path, and the cost of one layout becomes quadratic in flex nesting depth.
Measured with the test case below (one forced layout, Safari 27.0 / WebKit 605.1.15, Apple Silicon):
| flex nesting depth | flex: 1 (basis 0%) |
flex-basis: 0px |
ratio |
|---|---|---|---|
| 50 | 2 ms | 1 ms | 2× |
| 100 | 9 ms | 1 ms | 9× |
| 200 | 52 ms | 1 ms | 52× |
| 300 | 176 ms | 2 ms | 88× |
| 400 | 512 ms | 3 ms | 171× |
| 500 | 1129 ms | 5 ms | 226× |
Doubling the depth roughly quadruples the time with 0%, while 0px stays flat. The same test case on Chrome 153 is flat for both (~1–2 ms at every depth), so this is WebKit-specific.
In our production app (a large RN + react-native-web codebase, ~60 levels of nested flex on the main screen) this makes Safari 27 hang indefinitely — a single Document::updateLayout never returns and the tab is permanently unresponsive. A native sample of the WebKit.WebContent process shows 100% of the main thread in one ~11,700-frame-deep stack cycling through those four functions:
WebCore::Document::updateLayout
... RenderFlexibleBox::layoutBlock / RenderBlock::simplifiedLayout ...
WebCore::RenderFlexibleBox::canUseFlexItemForPercentageResolution
WebCore::RenderFlexibleBox::canComputePercentageFlexBasis<WebCore::Style::FlexBasis>
WebCore::RenderBox::computePercentageLogicalHeightGeneric<WebCore::Style::FlexBasis>
WebCore::RenderBlock::availableLogicalHeightForPercentageComputation
WebCore::RenderFlexibleBox::canUseFlexItemForPercentageResolution <-- cycles
Any JS that forces a synchronous layout detonates it (we saw it entered via innerText, and via scrollLeft after we removed the innerText read). Patching createReactDOMStyle to emit flex-basis: 0px fixed it completely: the app now boots and stays responsive on Safari 27.
Expected behavior
Safari 27 shouldn't significantly degrade the performance of RNW apps. I want to be upfront that I do not have a safe drop-in replacement to propose. Changing the emitted flex-basis unit is not behaviour-preserving, so this may need either a different approach or an upstream WebKit fix. I'm filing it because the performance characteristic seems worth knowing about regardless, and because a maintainer will have much better judgement than me about which trade-off is acceptable.
Steps to reproduce
- Open the test case
- Observe the results
Environment: Safari 27.0 (WebKit 605.1.15) on macOS. Also reproducible on iOS 27. Left screenshot is Chrome 153. react-native-web version doesn't matter, the createReactDOMStyle function hasn't changed the flex handling since 0.19.
The generated CSS is what matters, so the test case below is plain HTML/CSS with no react-native-web dependency. If you do not trust that the generated HTML+CSS is really from react-native-web, I can prepare a React version as well
Test case
https://gist.github.com/gigobyte/0fec52ae3c98c25f7f5468dd23281042
Additional comments
I've also logged a companion WebKit report - https://bugs.webkit.org/show_bug.cgi?id=324434
Source: necolas/react-native-web