Skip to content

Svg bounding box blocks touches despite parent View style={{pointerEvents: 'box-none'}} and Svg pointerEvents="none" #2690

Open
@msageryd

Description

@msageryd

Description

When an <Svg> component is nested within a standard React Native <View style={{pointerEvents: 'box-none'}}>, and the <Svg> itself has the pointerEvents="none" prop, its entire bounding box still blocks touch events from reaching elements rendered underneath the parent View. This blocking behavior occurs if the <Svg> contains any rendered child element (e.g., a purely visual <Circle fill="orange" />), even if that child element has no onPress handlers and no pointerEvents attributes (or pointerEvents="none").

The expected behavior is that the "empty" or non-painted areas of the <Svg> should allow touches to pass through to the underlying elements. This would respect the pointerEvents="box-none" behavior of its parent View (which correctly allows pass-through for its own empty space) and the pointerEvents="none" on the <Svg> tag itself (which should make the SVG canvas non-interactive and transparent to touches).

However, it appears that the presence of any rendered child content within the <Svg> causes the native SVG view to register its entire bounds as an active/occupied area with React Native's gesture system. This prevents the desired pass-through effect for the SVG's empty regions, effectively making the box-none strategy on the parent View ineffective for the area covered by the Svg's bounds.

Steps to reproduce

Create a React Native <View> and apply style={{ pointerEvents: 'box-none' }}. This View will act as a container for an overlay.

Place an <Svg> element as a child of this View. Set the pointerEvents="none" prop on the <Svg> element.
Inside the <Svg>, place a single, purely visual, non-interactive child element, for example,<Circle fill="orange" /> (no onPress handler, no pointerEvents="auto").

Render another interactive element (e.g., a pannable View or a Button) underneath the main View (i.e., rendered before it in the component tree or with a lower zIndex).

Minimal Code Example:

import React from 'react';
import {View, Alert, Text, PanResponder, StyleSheet} from 'react-native';
import Svg, {Circle} from 'react-native-svg';

const UnderlyingInteractiveElement = () => {
  const panResponder = React.useRef(
    PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onPanResponderGrant: () => Alert.alert('Underlying Element Touched!'),
      // Add some move handlers to visually confirm panning if desired
      onPanResponderMove: (_, gestureState) => {
        console.log(
          `Underlying move: dx=${gestureState.dx}, dy=${gestureState.dy}`,
        );
      },
    }),
  ).current;

  return (
    <View {...panResponder.panHandlers} style={styles.underlyingElement}>
      <Text>Underlaying area (Try to Pan/Touch)</Text>
    </View>
  );
};

const OverlaySvg = () => {
  const svgSize = 200;

  return (
    <View
      style={[
        styles.overlayContainer,
        {
          width: svgSize,
          height: svgSize,
          left: 50,
          top: 50,
        },
      ]}>
      <Svg
        width="100%"
        height="100%"
        viewBox={`0 0 ${svgSize} ${svgSize}`}
        pointerEvents="none" // Expect SVG canvas to be non-interactive AND allow pass-through
      >
        {/* A purely visual, non-interactive circle */}
        <Circle
          cx={svgSize / 2}
          cy={svgSize / 2}
          r={40}
          fill="orange"
          // No onPress handler
          // No pointerEvents="auto" or any pointerEvents prop
        />
      </Svg>
    </View>
  );
};

export default function App() {
  return (
    <View style={styles.container}>
      <UnderlyingInteractiveElement />
      <OverlaySvg />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  underlyingElement: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'lightblue',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 1, // Ensure it's "underneath"
  },
  overlayContainer: {
    position: 'absolute',
    // Visual debugging for the overlay container's bounds:
    borderColor: 'red',
    borderWidth: 1,
    pointerEvents: 'box-none', // Crucial: Expect pass-through for empty areas of this View
    // pointerEvents: 'none',   //"none" makes the view pass-through touches to underlying elements
    zIndex: 2, // Ensure it's "on top"
  },
});

Snack or a link to a repository

Dont know how to use RN SVG in a snack

SVG version

15.12.0

React Native version

0.79.2

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native

Architecture

Fabric (New Architecture)

Build type

Debug app & dev bundle

Device

iOS simulator

Device model

iPhone 16

Acknowledgements

Yes

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions