Repository navigation
Native-presented surfaces such as TrueSheet are not grabbable #10
Description
Describe the issue
React Native Grab cannot select React Native content rendered inside a separate native-presented surface. In a real Expo app, this occurs with @lodev09/react-native-true-sheet: while the sheet is visible, Grab still resolves the underlying ReactNativeGrabScreen as the selection owner.
I expected touching sheet content to highlight it and resolve its React component/Fiber. Instead, the sheet content is not highlighted or identified, so the copied context cannot tell which component rendered the touched element.
The underlying limitation appears to be that findNodeAtPoint starts from the active owner shadow node. A TrueSheet is presented in a separate platform-owned native container, outside that screen shadow subtree.
Which element is not grabbable?
Any React Native host element rendered inside a TrueSheet native bottom sheet, for example a Pressable, Text, or View in the sheet body. The same class of problem likely affects other libraries that present React content in a separate native surface.
Steps to reproduce
- Wrap the app root with
ReactNativeGrabRootand the Expo Router screen withReactNativeGrabScreen. - Render and present a
TrueSheetcontaining aPressableandText. - Open the Dev Menu and enable React Native Grab while the sheet is visible.
- Touch or drag over the sheet content.
- Observe that the native-presented content is not selected or mapped back to its React component.
Minimal reproduction
import { useRef } from "react";
import { Button, Pressable, Text, View } from "react-native";
import { TrueSheet } from "@lodev09/react-native-true-sheet";
import {
ReactNativeGrabRoot,
ReactNativeGrabScreen,
} from "react-native-grab";
function Screen() {
const sheet = useRef<TrueSheet>(null);
return (
<ReactNativeGrabScreen>
<Button title="Open sheet" onPress={() => sheet.current?.present()} />
<TrueSheet ref={sheet} detents={["auto"]}>
<View style={{ padding: 24 }}>
<Pressable>
<Text>Inspect me</Text>
</Pressable>
</View>
</TrueSheet>
</ReactNativeGrabScreen>
);
}
export default function App() {
return (
<ReactNativeGrabRoot>
<Screen />
</ReactNativeGrabRoot>
);
}A production repro uses this same structure through a shared BottomSheet wrapper in PoloMate.
React Native version
0.85.3
react-native-grab version
1.1.1
Routing library
Expo Router
Using native navigators?
Yes
Environment details
Expo SDK 56.0.8, React Native 0.85.3, Hermes, New Architecture/Fabric enabled, @lodev09/react-native-true-sheet 3.10.1. The failure is at the native-surface boundary rather than at a specific sheet child.
Logs / screenshots / recordings
No runtime exception is emitted. Grab continues using the underlying focused screen owner, whose shadow subtree does not include the TrueSheet native-presented surface.
Proposed direction
Introduce a supported way to register a temporarily active native-presented surface as a selection owner (or automatically discover such a surface), with lifecycle/priority semantics that do not steal selection while the sheet is dismissed. I am happy to contribute a regression test and implementation after confirming the preferred API shape.
Activity
huytdps13400 commentedon Aug 30, 2026
I opened #11 with a generic
ReactNativeGrabSurface active={isPresented}API. It registers the native-presented subtree only while active, prioritizes stacked surfaces over the focused screen/root, and restores the previous owner on deactivate or unmount. The regression suite covers stacked-surface priority and fallback; 13 tests, formatting, typecheck, and ESM/CJS build passed. Native TrueSheet device verification is still called out explicitly in the PR.huytdps13400 commentedon Aug 30, 2026
Native verification is now complete in #11. On the repository Expo/Fabric iOS example with TrueSheet 3.10.1, the active surface became the resolved selection owner, the target was highlighted,
View / Copy / Openrendered in the sheet, and Copy resolved the native host back to the exact React source file and line. The sheet also dismissed normally afterward. Full evidence is recorded in the PR: #11 (comment)