Skip to content

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".

Last updated · 470 distilled / 1,213 read