Skip to content

Native-presented surfaces such as TrueSheet are not grabbable #10

Description

@huytdps13400

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

  1. Wrap the app root with ReactNativeGrabRoot and the Expo Router screen with ReactNativeGrabScreen.
  2. Render and present a TrueSheet containing a Pressable and Text.
  3. Open the Dev Menu and enable React Native Grab while the sheet is visible.
  4. Touch or drag over the sheet content.
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions