CONCEPT Cited by 1 source
View flattening¶
Definition¶
View flattening is a React Native rendering optimization
that omits views from the native view tree when they are
deemed unnecessary for the final layout. A <View> that wraps
children but has no visual effect (no background, border,
transform, etc.) may be elided, and its children re-parented
directly to its grandparent. The optimization reduces native
view count, memory, and traversal cost.
Where it's applied¶
- Android — view flattening has been present since before React Native's new architecture.
- iOS — view flattening is new with Fabric (the new architecture's renderer). This is why the failure modes described below disproportionately appear on iOS during migration, even for apps that had worked correctly on Android for years.
See systems/react-native and sources/2025-09-12-shopify-migrating-to-react-natives-new-architecture.
The failure mode — refs resolve to null¶
A view that has a ref attached is a candidate for use by
imperative APIs — <View ref={ref}> so another component can
call ref.current.measure(), ActionSheetIOS.showActionSheetWithOptions
against it, etc. If view flattening removes the view, the
ref's target is gone, and ref.current is null when the
consumer tries to use it.
Shopify's writeup: "We found cases where components with refs
were being optimized out (<View ref={ref}>), causing the ref
to always be null. This created problems when other
components tried to use that ref, commonly seen with the
ActionSheetIOS API."
Secondary failure mode — end-to-end tests can't find elements¶
End-to-end testing tools (e.g., Appium) locate elements by
testID. If the view carrying the testID is flattened out,
the element isn't in the native tree and the test framework
can't find it.
The fix — collapsable={false}¶
collapsable={false} is an explicit opt-out that tells the
renderer "do not flatten this view even if you think you can".
Applied to any view that has a ref or a testID.
Shopify's approach scales this: they built a Babel plugin
that automatically sets collapsable={false} on any component
with an explicit testID. This removes the human error of
forgetting to opt out per-call-site. (See
patterns/babel-plugin-automatic-collapsable-false.)
For the ref case, the fix is still per-call-site: every view
carrying a ref needs collapsable={false}, and there's no
compile-time invariant (a custom ESLint rule could catch this
statically).
Why the optimization is still net-positive¶
Even with these failure modes, view flattening is a real
rendering win: deep view trees with lots of wrapper views are
the default React pattern (<View><View><View>...</View></View></View>),
and flattening materially cuts native view count on the hot
path. The solution isn't "turn off flattening"; it's "tell
the renderer which views must survive the optimization".
Related¶
- systems/react-native — where view flattening lives.
- concepts/state-batching — another new-architecture behavior that exposes latent issues in consumer code.
- patterns/babel-plugin-automatic-collapsable-false — compile-time fix for the testID-flattening failure.