From 9aadafb31c99a2b417bdd3bba7c526484e2d7570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kelmanson?= Date: Tue, 16 Jun 2026 00:43:27 -0300 Subject: [PATCH] fix(android): embedded surface renders with zero height on RN 0.85 On RN 0.85 the embedded sandbox surface stays blank: the component runs ("Running ") 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 . 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) --- .../rnsandbox/SandboxReactNativeView.kt | 16 +++++++++++++++- packages/react-native-sandbox/src/index.tsx | 9 +++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/react-native-sandbox/android/src/main/java/io/callstack/rnsandbox/SandboxReactNativeView.kt b/packages/react-native-sandbox/android/src/main/java/io/callstack/rnsandbox/SandboxReactNativeView.kt index 3729790..22fb640 100644 --- a/packages/react-native-sandbox/android/src/main/java/io/callstack/rnsandbox/SandboxReactNativeView.kt +++ b/packages/react-native-sandbox/android/src/main/java/io/callstack/rnsandbox/SandboxReactNativeView.kt @@ -78,7 +78,21 @@ class SandboxReactNativeView( val w = right - left val h = bottom - top for (i in 0 until childCount) { - getChildAt(i).layout(0, 0, w, h) + val child = getChildAt(i) + // The child is a ReactSurfaceView from a SEPARATE ReactHost. Fabric + // lays our view out by calling layout() directly, without a measure + // pass, so the child never receives onMeasure(). But ReactSurfaceView + // only pushes layout constraints to its surface from onMeasure() (and + // its onLayout() is a no-op until wasMeasured is true). Without an + // explicit measure the nested surface keeps its initial constructor + // constraints and renders blank (regression surfaced on RN 0.85). + // Measure with EXACTLY specs so updateLayoutSpecs() runs with our real + // size, then lay the child out to fill us. + child.measure( + MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY), + ) + child.layout(0, 0, w, h) } } diff --git a/packages/react-native-sandbox/src/index.tsx b/packages/react-native-sandbox/src/index.tsx index c01386e..c3760b4 100644 --- a/packages/react-native-sandbox/src/index.tsx +++ b/packages/react-native-sandbox/src/index.tsx @@ -7,7 +7,7 @@ import { useRef, } from 'react' import type {NativeSyntheticEvent} from 'react-native' -import {StyleProp, StyleSheet, View, ViewProps, ViewStyle} from 'react-native' +import {StyleProp, View, ViewProps, ViewStyle} from 'react-native' import type {NativeSandboxReactNativeViewComponentType} from '../specs/NativeSandboxReactNativeView' import NativeSandboxReactNativeView, { @@ -305,9 +305,14 @@ const SandboxReactNativeView = forwardRef< return null }, []) + // 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. const _style: StyleProp = useMemo( () => ({ - ...StyleSheet.absoluteFillObject, + flex: 1, }), [] )