In a React Native Pressable, in what order do onPressIn, onPressOut, onPress and onLongPress fire, and when is onPress skipped?
answer
- in, out, then press
- 500 ms while still down
- long press swallows the tap
- only if onLongPress is passed
- slide off or scroll: out, no press
basics
~20 sA tap fires onPressIn, onPressOut, then onPress. Holding past delayLongPress (500 ms by default) fires onLongPress while the finger is down; if onLongPress is set, the release gives onPressOut but no onPress. Sliding off or a parent scroll also skips onPress.
solid answer
~40 s`onPressIn` fires when the touch activates, and `onPressOut` fires when the press stops being active. `onPress` follows only for a completed tap, meaning the finger lifted inside the press area. If the finger stays down for `delayLongPress` (500 ms by default), `onLongPress` fires while it is still down. When an `onLongPress` handler exists, that gesture then ends with `onPressOut` and no `onPress`; with only `onPress` passed, a long hold still ends in `onPress`. `onPress` is also skipped when the finger slides past the press area before lifting, when a scrolling parent takes the touch (the press is cancelable by default), and when `disabled` is true. So a calculator's delete key can put delete-one-digit on `onPress` and clear-all on `onLongPress` without the two colliding.
code
tsx · 18 linesimport {Pressable, Text} from 'react-native';
type DeleteKeyProps = {
onDeleteDigit: () => void;
onClearAll: () => void;
};
export function DeleteKey({onDeleteDigit, onClearAll}: DeleteKeyProps) {
return (
<Pressable
onPress={onDeleteDigit}
onLongPress={onClearAll}
delayLongPress={600}
style={({pressed}) => ({padding: 24, opacity: pressed ? 0.6 : 1})}>
<Text>DEL</Text>
</Pressable>
);
}go deeper
Recall the tap order, onPressIn then onPressOut then onPress, and that onLongPress fires after 500 ms by default while the finger is still down.
Explain every way onPress is skipped: an onLongPress handler that fired, a release outside the press area, a parent scroll taking the touch, and disabled. Say which callback is right for feedback versus the action.
Know the edges that cause production bugs: a long hold without onLongPress still ends in onPress, drift beyond 10 points cancels the long press, and a very quick tap can run onPress before onPressOut.
Treat press semantics as part of the product spec: decide per control whether actions commit on press-in or on release, and make a shared key primitive enforce that choice consistently.
## The press lifecycle in one picture `Pressable` (from `react-native`) turns a touch into a small sequence of callbacks. Knowing the order, and the cases where one is skipped, is what separates a key that behaves from one that deletes two digits when the user meant one. All four callbacks receive a press event; none of them fire when `disabled` is true, because a disabled `Pressable` never becomes the touch responder. A plain tap produces: 1. **`onPressIn`**: the finger is down and the press is active; `pressed` becomes `true`. 2. **`onPressOut`**: the press stops being active; `pressed` becomes `false`. 3. **`onPress`**: the tap is confirmed because the finger lifted inside the press area. A long hold produces: 1. **`onPressIn`** when the finger goes down. 2. **`onLongPress`** once the finger has stayed down for `delayLongPress` milliseconds, **while it is still down**. The default is **500 ms**, measured from the start of the press. 3. **`onPressOut`** when the finger lifts. 4. **No `onPress`**, provided an `onLongPress` handler was passed. ## When onPress does not fire The rules live in React Native's internal **Pressability** state machine, which `Pressable` configures. `onPress` is skipped when: - **The long press already fired and you passed `onLongPress`.** The source marks the press as "canceled by long press" only when an `onLongPress` handler exists. If you pass only `onPress`, a two-second hold still ends in a normal `onPress` on release. - **The finger lifted outside the press area.** Sliding past the press area (the view, plus `hitSlop`, plus `pressRetentionOffset`) fires `onPressOut` immediately and cancels the long-press timer; releasing out there fires nothing more. Sliding back in before lifting reactivates the press (`onPressIn` again), and a release inside then counts. - **A parent took the gesture.** `Pressable` is `cancelable` by default, so a scrolling parent can take over the touch. The press is terminated with `onPressOut` and no `onPress`. - **`disabled` is true.** No callback runs. There is one more subtle cancellation. If the finger moves **more than 10 points** from where the press activated, the long-press timer is cancelled even though the finger is still inside the press area. The press itself survives: lifting then fires `onPress`, not `onLongPress`. ## Timing details worth knowing | Knob | Default | Effect | |---|---|---| | `delayLongPress` | 500 ms | time from press start to `onLongPress` | | minimum press duration (internal) | 130 ms | holds back `onPressOut` so a quick tap still shows `pressed` for a moment | | `unstable_pressDelay` | none | waits before `onPressIn`; the `unstable_` prefix marks it as not a stable API | The internal minimum press duration has a consequence the docs gloss over. The docs describe `onPress` as "called after `onPressOut`", and for a normal press that holds. For a tap shorter than about 130 ms, though, the implementation schedules `onPressOut` for later and runs `onPress` straight away, so `onPress` can run **before** `onPressOut`. Do not write `onPress` logic that assumes `onPressOut` has already cleaned something up. ## A calculator delete key A calculator's delete key is the classic place to use both callbacks: a tap removes the last digit, a hold clears the display. Because `onLongPress` is passed, a hold clears the display and the release does not also delete a digit. If you forgot `onLongPress` and tried to detect the hold by timing `onPressIn` to `onPressOut` yourself, you would also get an `onPress` on every release and delete one digit too many. Where to put work: - **Visual feedback**: the `pressed` value in a `style` or `children` function, or `onPressIn` and `onPressOut` for effects that live outside the component. - **The action**: `onPress`, because it fires only for a completed tap. - **Secondary actions**: `onLongPress`, remembering it suppresses `onPress` for that gesture. - **Anything that must stop when the finger stops**: `onPressOut`, because every end of an active press passes through it. ## Common mistakes - Doing the main action in `onPressIn`, which fires even when the user slides away or scrolls instead of tapping. - Assuming `onLongPress` fires on release. It fires while the finger is still down. - Expecting `onPress` after every `onPressIn`. Slide-offs, parent scroll takeovers and long presses all end without it.
- Does onPress always run after onPressOut in a React Native Pressable?Not on a very quick tap. Pressability holds back `onPressOut` so the pressed state lasts at least about 130 ms, while `onPress` runs as soon as the finger lifts. A tap shorter than that can therefore see `onPress` before `onPressOut`. Keep the two independent rather than relying on their order.
- Why might onLongPress never fire for a user with an unsteady thumb?Pressability cancels the long-press timer once the finger moves more than 10 points from where the press activated, even if it is still inside the press area. The press itself survives, so the release fires `onPress` instead. A generous `delayLongPress` makes the drift window longer, not shorter.
- What happens if the finger slides off a Pressable and then back on before lifting?Leaving the press area fires `onPressOut` and cancels the long-press timer. Coming back reactivates the press, so `onPressIn` fires again, and lifting inside then fires `onPress`. A long press that already fired is not repeated, and the release after it still skips `onPress` when an `onLongPress` handler exists.
A doorbell with a hold-to-talk mode: a short press rings the bell, but once you have held it long enough to start talking, letting go ends the call and does not ring the bell as well.
saying these in an interview costs you the question
- onPress fires as soon as the finger touches the Pressable.
- onLongPress fires when the finger lifts after a long hold.
- onPress always follows onPressOut, even after onLongPress has fired.
- Without an onLongPress handler, a long hold cancels onPress.
- Releasing outside the Pressable still counts as a press because it started inside.