fix(android): embedded surface renders with zero height on RN 0.85#38
fix(android): embedded surface renders with zero height on RN 0.85#38akelmanson wants to merge 1 commit into
Conversation
On RN 0.85 the embedded sandbox surface stays blank: the component runs
("Running <name>") but the native SandboxReactNativeView is laid out with
height 0. Two causes, both fixed here:
1. The native view is wrapped as position:absolute (StyleSheet.absoluteFillObject)
inside the user-styled <View>. On 0.85 an absolutely-positioned child with
all-zero insets no longer stretches to a flex-sized parent and collapses to
height 0 (width still comes from the parent's default alignItems:stretch,
masking it). Make the in-flow child flex:1 so it reliably claims the
wrapper's space.
2. onLayout positioned the nested ReactSurfaceView via layout() but never
measured it. ReactSurfaceView only pushes layout constraints to its surface
from onMeasure() (its onLayout() is a no-op until wasMeasured). Measure the
child with EXACTLY specs so updateLayoutSpecs() runs with the real size.
Verified on a device (RN 0.85.3): the embedded surface renders and lays out.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| // The native sandbox view fills the user-styled wrapper as an in-flow | ||
| // flex child. It used to be position:absolute (StyleSheet.absoluteFillObject), | ||
| // but on RN 0.85 an absolutely-positioned child with all-zero insets no longer | ||
| // stretches to a flex-sized parent — it collapses to height 0 and the embedded | ||
| // surface renders blank. flex:1 makes it claim the wrapper's space reliably. |
There was a problem hiding this comment.
To be on the same page it work without issueson pre 0.85 too, right?
There was a problem hiding this comment.
Tried applying fix on android and ios RN80 demo apps:
Ios baseline (android baseline same, just diff backing color)

AI-assisted analysis
Finding: flex: 1 introduces layout regression on pre-0.85 apps (Android + iOS)
The change from StyleSheet.absoluteFillObject to flex: 1 alters how the native sandbox view interacts with its parent wrapper's padding.
Before (absoluteFillObject): The native view stretches to fill the entire wrapper bounds, ignoring any padding set on the wrapper <View style={style}>.
After (flex: 1): The native view participates in normal flex layout and respects the parent's padding, making the sandbox content area narrower/shorter.
Root cause
// In the library's index.tsx:
<View style={style}> {/* ← user's style, may include padding */}
<NativeSandboxReactNativeView style={_style} /> {/* ← was absoluteFill, now flex:1 */}
</View>absoluteFillObject (position absolute + zero insets) ignores the parent's padding.
flex: 1 (in-flow child) respects it. Any consumer with padding on their sandbox component style will see a layout change.
Recommendation
The fix is correct for solving the RN 0.85 zero-height issue, but it's a breaking layout change for existing consumers. Consider one of:
- Strip padding from the inner wrapper — apply the user's style but override padding to 0 on the wrapper, or split into an outer container (user style) and inner container (no padding, holds the native view)
- Use width: '100%', height: '100%' instead of flex: 1 — this fills the content area explicitly without requiring flex participation, and behaves more like the old absolute approach while still being in-flow
- Document as a breaking change — if the old behavior (ignoring padding) was unintended, note it in release notes so consumers can remove padding from their sandbox styles


Summary
On RN 0.85 the embedded sandbox surface stays blank on Android: the bundle runs (
Running "<name>"shows in the log) but the nativeSandboxReactNativeViewis laid out with height 0 (width still fills via the parent's defaultalignItems: stretch, which masks the problem). The lib works on RN 0.80 — this is a 0.85 regression.Two independent causes, both fixed:
absoluteFillObjectcollapses to height 0. The native view is rendered asposition:absolute(StyleSheet.absoluteFillObject) inside the user-styled<View>wrapper. On RN 0.85 an absolutely-positioned child with all-zero insets (top/left/right/bottom: 0) no longer stretches to a flex-sized parent — it collapses to height 0. Making it an in-flowflex: 1child makes it reliably claim the wrapper's space.Nested
ReactSurfaceViewnever measured.onLayoutpositioned the child withlayout()but never calledmeasure().ReactSurfaceViewonly pushes layout constraints to its surface fromonMeasure()(itsonLayout()is a no-op untilwasMeasuredis true), so the nested surface kept its initial constructor constraints and rendered blank. We nowmeasure()the child withEXACTLYspecs before laying it out, soupdateLayoutSpecs()runs with the real size.Test plan
Verified on a device (RN 0.85.3 / React 19.2.3, New Architecture, Android/Waydroid x86_64): the embedded surface renders, lays out to fill its container, and receives touches. Before the change
onLayoutreportedw=742 h=0; after,w=742 h=919and the surface paints.🤖 Generated with Claude Code