In React Native, what do the four View pointerEvents values do, and which lets taps pass through an overlay while its buttons work?
answer
- hit-testing, not visibility
- auto, none, box-none, box-only
- none excludes the whole subtree
- box-none: children only
- box-only: the view only
basics
~20 spointerEvents sets whether a React Native View and its children can be touch targets: 'auto' (both), 'none' (neither), 'box-none' (children only) and 'box-only' (the view only). An overlay uses 'box-none' so empty areas pass taps through.
solid answer
~40 s`pointerEvents` decides which views can be the **target** of a touch; it changes hit-testing, not drawing. `'auto'` is normal behaviour. `'none'` means neither the view nor anything inside it can be a target, so touches fall through to whatever is underneath. `'box-none'` means the view itself is never a target but its children can be: the right choice for a full-screen `position: 'absolute'` layer holding a floating button above a scrolling poem, because taps and drags on the empty area reach the content below while the button still works. `'box-only'` is the reverse: the view can be a target but its children cannot, useful when a whole card should handle the press and inner controls must not. In 0.87 it is both a `View` prop and a style property.
code
tsx · 23 linesimport {Pressable, ScrollView, StyleSheet, Text, View} from 'react-native';
export function PoemScreen({poem, onAnnotate}: {poem: string; onAnnotate: () => void}) {
return (
<View style={styles.screen}>
<ScrollView>
<Text style={styles.poem}>{poem}</Text>
</ScrollView>
{/* The layer covers the screen but only its children take touches. */}
<View style={StyleSheet.absoluteFill} pointerEvents="box-none">
<Pressable style={styles.fab} onPress={onAnnotate}>
<Text>Annotate</Text>
</Pressable>
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: {flex: 1},
poem: {fontSize: 18, lineHeight: 28, padding: 16},
fab: {position: 'absolute', right: 16, bottom: 24, padding: 12},
});go deeper
Recall the four values and that pointerEvents controls whether a view can be touched, not whether it is visible.
Explain how each value treats the view and its subtree, and why a transparent overlay still blocks touches until it gets box-none.
Diagnose touch-swallowing overlays, choose box-none or box-only deliberately, and separate hit-testing problems from responder conflicts between gestures.
Set conventions for floating layers and disabled regions across the app so overlays never silently block content, and review them as part of screen design.
## What pointerEvents controls When the user touches the screen, the platform performs **hit-testing**: it walks the view hierarchy to find the view under the finger that should receive the touch. `pointerEvents` lets a React Native `View` take itself, its children, or both out of that search. It does not change rendering: a view with `pointerEvents: 'none'` is still fully visible. It is accepted both as a **`View` prop** (`<View pointerEvents="box-none">`) and as a **style property** (`style={{pointerEvents: 'box-none'}}`). Components built on `View`, such as `Pressable`, accept it too, and `Text` has its own `pointerEvents` prop. ## The four values | Value | The view itself | Its descendants | Typical use | |---|---|---|---| | `'auto'` | can be a target | can be targets | normal behaviour | | `'none'` | never a target | never targets | decorative overlays, disabled regions | | `'box-none'` | never a target | can be targets | full-screen layers holding floating controls | | `'box-only'` | can be a target | never targets | a card that handles the press as one unit | Two details matter: - **`'none'` applies to the whole subtree.** A `Pressable` inside a `pointerEvents="none"` view cannot be pressed; touches go to whatever lies beneath the overlay. - **`'box-none'` is not the same as `'none'` on the parent and `'auto'` on children.** It is the one value that expresses "ignore my own empty area, keep my children interactive". ## The overlay scenario A poem reader shows the poem in a `ScrollView`. Above it, a floating **Annotate** button sits in the bottom-right corner. The easiest way to place it is a layer that covers the screen: 1. Render the `ScrollView`. 2. Render, after it, a `View` with `StyleSheet.absoluteFill` so it covers the screen, containing the button positioned at the bottom right. With the default `'auto'`, the layer is a valid touch target across the whole screen. Because it is on top, **it receives every touch**: the poem no longer scrolls, and inline links in the poem stop responding, even though the layer is transparent. Transparency has nothing to do with hit-testing. - `pointerEvents="none"` on the layer lets touches reach the poem, but also **disables the button**, because `'none'` covers descendants. - `pointerEvents="box-none"` on the layer makes its empty area transparent to touches while the **button stays pressable**. This is the fix. ## Where box-only helps `'box-only'` suits a component that must behave as one touchable unit even though it contains interactive children, for example a "locked" poem tile that shows a preview, including inline links, but should respond to any tap by opening a subscription screen. Setting `'box-only'` on the tile stops the inner links from ever receiving the touch, so the tile's own handler runs instead. ## Diagnosing a view that swallows touches When a region of the screen stops responding, the cause is usually a view on top of it that is a valid touch target: 1. Temporarily give suspected overlays a visible `backgroundColor` to see exactly what they cover. 2. Check the render order and `zIndex`: the overlay is often a later sibling or an absolutely positioned child of a parent that fills the screen. 3. Decide what the overlay needs: no interaction at all means `'none'`; interactive children but an inert background means `'box-none'`. 4. Re-test both the content below and the overlay's own controls, since fixing one by breaking the other is the usual regression. ## Common mistakes - **Using opacity to disable touches.** `opacity: 0` hides a view but leaves it a touch target; an invisible view can swallow taps. - **Putting `'none'` on a container with buttons in it**, then wondering why the buttons stopped working. - **Expecting `pointerEvents` to arbitrate between two handlers** that both want the same gesture. That negotiation, which view becomes the responder, is a different mechanism. - **Forgetting the stacking order.** Hit-testing starts from the top-most view, so a later sibling or a view with a higher `zIndex` is tested first.
- In React Native, why does a fully transparent absolute overlay stop a ScrollView beneath it from scrolling?Hit-testing ignores colour and opacity. With the default `pointerEvents` of `'auto'`, the overlay is a valid target everywhere it covers, and because it is on top it is found first, so it takes the touches the `ScrollView` needed. Setting `pointerEvents` to `'box-none'`, or `'none'` if it has no interactive children, lets the touches reach the content below.
- In React Native, is pointerEvents a View prop or a style property?Both. `View` accepts `pointerEvents` as a prop with the values `'auto'`, `'none'`, `'box-none'` and `'box-only'`, and the same values are accepted as a style property in `ViewStyle`. Either form controls hit-testing for that view; teams usually pick one form and use it consistently.
A box-none layer is a glass sheet with buttons glued to it: fingers pass straight through the glass to the page underneath, but anything glued on top can still be pressed.
saying these in an interview costs you the question
- pointerEvents 'none' only affects the view itself; its buttons still receive taps.
- A transparent or opacity 0 view never receives touches, so no extra prop is needed.
- pointerEvents hides the view or changes how it is drawn.
- 'box-only' lets touches pass through the view to its children.
- pointerEvents decides which of two nested handlers wins the same gesture.