Articles · GuidesUpdated October 2026

Your generated app has two gesture systems in it, and nobody told you which one is on.

React Native ships a touch system of its own, in which views negotiate who owns a finger. Most real projects also install react-native-gesture-handler, which recognises gestures natively instead. Both can be present in the same codebase, on different screens, and the two sets of documentation do not mention each other once. So when a swipe does nothing, the first job is not fixing the gesture. It is working out which system was supposed to be handling it. Every quote below comes from React Native's own reference or from Expo's, read on 17 October 2026.

What follows is how to tell which one your project uses, the numbers the built in system publishes, why a swipe loses a fight with a scroll, and the one piece of setup that makes gestures fail silently. If the framework itself is still new to you, start with building React Native apps without coding and come back to this.

Start here

The short version

One system negotiates. The other one recognises.

React Native's built in model is a negotiation. A view asks to become the responder for a touch, other views can ask to take it, and the framework decides. That is why the handler names are all about asking and granting and terminating rather than about taps and swipes. It is also, in the documentation's own words, complicated to use, which is why Pressable and the Touchable components exist on top of it.

The gesture library works the other way round. You declare a tap, a pan or a rotation, and it recognises that shape for you. Expo's page quotes the library's README: the logic runs 100 percent in the native thread and is therefore deterministic. Neither description is marketing. They are two different places to put the decision, and your project may well be using both.

Which gesture system is your app actually using?

Open package.json and look for react-native-gesture-handler. If it is not there, every gesture in the app is running on what React Native ships, and the only files that matter are the ones with Pressable, Touchable or PanResponder in them. If it is there, both systems are available and the per screen answer is whatever that screen imports. A file with GestureDetector in it is using the library. A file with onPress in it is using the built in path. They can sit next to each other in the same app without complaint.

Being in package.json is not the same as being switched on, which is the detail that wastes the most time. Expo's gestures tutorial says that to get gesture interactions to work you render GestureHandlerRootView at the top of the component and replace the root level View with it. Installed library, missing wrapper, dead gestures, no error message. That combination accounts for a large share of the gestures people describe as broken.

The last thing to check is whether the interaction needs a library at all. Taps, holds and scrolling are all covered by what the framework already has, and the Handling Touches page sends swipes and pans that scroll a list straight to ScrollView. A dependency you do not need is still a dependency you have to upgrade. Tick what your screen has to do and see where the documentation points.

Check yours

Does this interaction need a gesture library at all?

Tick what the screen has to do. The answers name what each side's own documentation points at, so you can go and read the right page.

Nothing ticked. Most screens in most apps are taps and scrolls, and those need no gesture library at all.

What does React Native do on its own, with nothing installed?

More than people expect, and with real numbers attached. Pressable is described as a Core Component wrapper that can detect the various stages of press interactions on its children. The order is documented: onPressIn fires immediately when a touch is engaged, onPressOut when it is released, and onPress after onPressOut. Hold longer than the delayLongPress default of 500 milliseconds and onLongPress fires instead, with onPressOut still arriving when the finger lifts.

Two of those numbers are the reason a cheap looking app feels cheap. Pressable has a HitRect, set with hitSlop, which defines how far from the element a press can still register, because fingers are not precise and people miss. It also has a PressRect, set with pressRetentionOffset, which lets a press stay alive while the finger slides off the control. Its documented default is 30 at the bottom and 20 on the top, left and right, read 17 October 2026. hitSlop publishes no default at all. A button with no hitSlop in a cramped row is a button people will miss, and the fix is a prop rather than a redesign.

Underneath that is the responder lifecycle, and it is worth reading once even if you never write one. onStartShouldSetResponder asks whether a view wants the touch. onResponderGrant is described as the time to highlight and show the user what is happening. onResponderMove and onResponderRelease carry the finger and the lift. onResponderTerminationRequest lets another view ask for the touch, and onResponderTerminate reports that it has gone, possibly taken by the operating system without asking, which the page says happens with control centre and notification centre on iOS. For anything that has to interpret a drag rather than a press, the page points at PanResponder, which it says reconciles several touches into a single gesture and hands you a gestureState carrying dx, dy, vx, vy and the number of active touches.

The page also states two expectations as best practice, and they are the parts a generated screen most often skips. Every action should have feedback, showing the user what is handling their touch and what will happen when they release. And every action should be cancellable, so the user can abort it mid touch by dragging their finger away. If a tap in your app does nothing visible while a request runs, that is not a slow network problem. It is a missing onPressIn.

React Native, Gesture Responder System, page last updated 12 August 2026, read 17 October 2026

Why does a swipe lose a fight with a scroll?

Because only one view can own a finger, and the rules for who gets it are written down. The ShouldSetResponder handlers are called with a bubbling pattern, deepest node first, which means the deepest component wins when several of them say yes. That is a sensible default and it is exactly wrong for a swipeable row, where the thing you want to win is often the parent. The documented answer is the capture handler: a parent that wants to stop a child from becoming the responder on a touch start should implement onStartShouldSetResponderCapture and return true.

This is why swipe to delete and drag to reorder are the two features that most often come back half working from any code generator, human or otherwise. They are not hard to write. They are hard to arbitrate, because the correct behaviour depends on the angle of the finger in the first few milliseconds, and no specification you wrote in plain English contained that. Expect to describe it again after you have used it.

There is one more place the touch can go that has nothing to do with your layout. React Native notes that the touch area never extends past the parent view bounds, and that the z index of sibling views always takes precedence when a touch hits two overlapping views. A transparent view sitting on top of your button, which is invisible in a screenshot and obvious in the code, will eat every tap. When a control looks right and does nothing at all, that is the first thing to look for, well before debugging a crash that is not happening.

The gesture library does not make the arbitration problem go away. It moves it. Instead of views negotiating in your component tree, native recognisers decide, with the determinism Expo's page advertises. That is usually a better place for the decision to live, especially for anything involving two fingers. It is still a decision somebody has to specify.

What actually breaks when a gesture is added later?

The silent failure comes first, and it is the root wrapper. A gesture built with the library and rendered outside GestureHandlerRootView does nothing and says nothing. No warning, no red screen, no log line. People then rewrite the gesture several times before looking at the top of the app, which is where the problem is. Expo documents the wrapper as the first step of adding gestures at all, so treat a dead gesture as a setup question before you treat it as a code question.

The second thing is the shape of the fix. Expo lists react-native-gesture-handler as included in Expo Go and installs it with npx expo install, which makes it feel like an ordinary package. It is not quite: it carries native code, and native code lives in the binary. A change that adds or upgrades it is a new build going to testers, not a JavaScript bundle you can push on top of the app they already have. That boundary is worth understanding before you promise a fix today, and it is the same boundary that decides what over the air updates can and cannot carry.

The third is the half finished state. Because a responder can be taken by the operating system without asking, every gesture that moves something needs an answer to the question of what happens if it ends early. A sheet dragged halfway and abandoned has to settle somewhere. A reorder interrupted by a phone call has to leave the list in a valid order. This is the work that separates an app that feels finished from one that feels like a demo, and it is almost never in the original description of the feature.

Last, gestures are the part of an app that a reviewer touches with their own hands. Nothing in either store's rules is about gesture libraries, but a control that does not respond reads as a broken app to whoever is holding the phone, and that is a judgement made in seconds. Walk every interactive element on a real device before publishing to the App Store, and tap the things you never tap.

Expo, react-native-gesture-handler reference, read 17 October 2026

The two systems, as their own documentation describes them

Every cell below is what one of these two sets of pages says about itself, read on 17 October 2026. The last row is the one worth noticing. Neither side mentions the other, so any sentence you read comparing them came from somewhere else.

The questionWhat React Native shipsreact-native-gesture-handlerSame answer for both
What its own documentation calls itThe Gesture Responder SystemA library that provides an API for handling complex gesturesNo
Arrives with the project whether you asked or notYesNo, it is an install: npx expo install react-native-gesture-handlerNo
States where the recognition runsNo, the pages say nothing about threadsYes, 100 percent in the native thread and therefore deterministicNo
Works inside Expo GoYesYesYes
Needs a wrapper component at the root of the appNoGestureHandlerRootView, per Expo's gestures tutorialNo
Publishes a default long press durationYes, 500 milliseconds on PressableNot on the Expo pages checkedNo
Mentions the other system anywhere on the pages checkedNoNoYes

Where this lands if you did not type the project

Nothing above changes because an agent wrote the files. The documentation is the same documentation, the 500 millisecond long press is the same number, and the missing root wrapper fails in the same silent way. What does change is how you find out. You did not choose between the two systems, so your first move on any gesture question is reading what the project already does rather than deciding what it should do.

Newly generates a real React Native and Expo project that you own, with two way GitHub sync, so that reading is a normal read of a normal repository. Search for GestureDetector and for onPress and you have the map in a minute. It builds and ships too: iOS builds go up to TestFlight, Android goes to Google Play internal testing and to a standalone APK you can hand someone directly. The price is $25 a month and there is no free tier. The honest limit is the one Apple sets, rather than the one the tool sets. Apple requires the owner of an app's content to submit it, so the upload is automated and the submission is yours.

The practical habit is simple. Describe gestures by what should happen when they go wrong, not only by what should happen when they go right. Say where a half dragged sheet settles. Say which of the swipe and the scroll wins. Say what the button looks like while the finger is down. Those three sentences are the difference between a screen that works on the first try and one you keep sending back, whoever is writing the code.

Questions people ask about gestures in a generated app

Two things can, and a generated project often has both sitting in it. React Native ships a gesture responder system of its own, in which views negotiate which one owns a touch, plus Pressable and the older Touchable components built on top of it. Separately, many projects install react-native-gesture-handler, which Expo describes as a library that provides an API for handling complex gestures. The first one is already there. The second one is a line in package.json. Which one a given screen uses is a question about your code, not about React Native.

Describe the gesture, including how it fails

Ask for the swipe, then say what happens when the finger lifts in the wrong place. The second sentence is the one that saves the rebuild.

Start building