Skip to main content

React Native Gesture Handler 3: Setup, Examples, and Migration

· 8 min read
Full Stack Developer

React Native Gesture Handler 3 tap, pan, and pinch gesture illustration

react-native-gesture-handler gives React Native apps native-driven tap, pan, pinch, rotation, and other touch interactions. The important part in 2026 is choosing the API that matches the version installed in your project: Gesture Handler 3 uses hooks such as useTapGesture, while many existing apps and Expo SDKs still use the Gesture Handler 2 Gesture.* API.

This guide shows the current setup, two complete Gesture Handler 3 examples, and the migration path from the previous API.

Quick answer

  1. Check the installed versions of React Native, Expo, Gesture Handler, Reanimated, and Worklets.
  2. Let Expo select compatible versions when you use Expo.
  3. Wrap the app as close to its root as possible with GestureHandlerRootView.
  4. For Gesture Handler 3, build gestures with hooks such as useTapGesture and usePanGesture.
  5. Use Reanimated and Worklets for interactions that must stay smooth on the UI thread.

The official compatibility table is the source of truth. At the time of this update, Gesture Handler 3 requires React Native 0.82 or newer, and the 3.2.x line supports React Native 0.83 through 0.87. The versions bundled by Expo may differ, so do not force a major upgrade over the version selected by expo install.

Choose the right API before changing code

Check the package versions in the app directory:

npm ls react-native react-native-gesture-handler react-native-reanimated react-native-worklets

Use the result to choose the correct path:

Installed versionAPI to use
Gesture Handler 3.xHook API: useTapGesture, usePanGesture, and relation hooks
Gesture Handler 2.xBuilder API: Gesture.Tap(), Gesture.Pan(), and Gesture.Simultaneous()
Older component APIPlan a staged migration; do not mix it with Gesture Handler 3 relations

Gesture Handler 3 cannot create relations between hook-based gestures and gestures from the previous API. Migrate one connected gesture group at a time. The official v3 migration guide lists every renamed API and relation.

Install with Expo

Let Expo choose the Gesture Handler version compatible with the current SDK:

npx expo install react-native-gesture-handler

For UI-thread animations, install the Expo-compatible Reanimated and Worklets packages as well:

npx expo install react-native-reanimated react-native-worklets

Current Expo projects configure the Worklets Babel plugin through babel-preset-expo. If you use a development build and add or change native dependencies, regenerate and rebuild the native projects:

npx expo prebuild

See the current Expo package pages for Gesture Handler and Reanimated before pinning versions manually.

Install with React Native Community CLI

For a current React Native app using Gesture Handler 3 and Reanimated 4:

npm install react-native-gesture-handler react-native-reanimated react-native-worklets
npx pod-install ios

Reanimated 4 requires the React Native New Architecture. In a Community CLI project, add the Worklets plugin last in babel.config.js:

babel.config.js
module.exports = {
presets: ["module:@react-native/babel-preset"],
plugins: [
// Other Babel plugins go first.
"react-native-worklets/plugin",
],
};

If your app still uses the old architecture, check the Reanimated compatibility documentation before selecting a Reanimated version. Do not upgrade all three libraries independently and hope their native code agrees at build time.

Add GestureHandlerRootView

Every gesture must be below a GestureHandlerRootView. Keep it as close to the actual application root as possible:

App.tsx
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {AppNavigator} from './src/navigation/AppNavigator';

export default function App() {
return (
<GestureHandlerRootView style={{flex: 1}}>
<AppNavigator />
</GestureHandlerRootView>
);
}

If gestures inside an Android Modal do not activate, wrap the modal content in another GestureHandlerRootView. Nested root views are otherwise ignored in favor of the top-most root.

Tap example with Gesture Handler 3

Gesture Handler 3 replaces Gesture.Tap() with useTapGesture. The callback runs on the UI thread when Reanimated integration is enabled, so a shared value can update without waiting for React to render.

PressableCard.tsx
import {StyleSheet, Text} from 'react-native';
import {
GestureDetector,
useTapGesture,
} from 'react-native-gesture-handler';
import Animated, {
useAnimatedStyle,
useSharedValue,
withSpring,
} from 'react-native-reanimated';

export function PressableCard() {
const scale = useSharedValue(1);

const tap = useTapGesture({
onBegin: () => {
scale.value = 0.96;
},
onFinalize: () => {
scale.value = withSpring(1);
},
});

const animatedStyle = useAnimatedStyle(() => ({
transform: [{scale: scale.value}],
}));

return (
<GestureDetector gesture={tap}>
<Animated.View style={[styles.card, animatedStyle]}>
<Text style={styles.label}>Tap me</Text>
</Animated.View>
</GestureDetector>
);
}

const styles = StyleSheet.create({
card: {
borderRadius: 16,
backgroundColor: '#17171b',
padding: 20,
},
label: {
color: '#ffffff',
fontWeight: '700',
},
});

Pan example with Gesture Handler 3

For a draggable card, use the accumulated translationX and translationY values supplied by the pan event:

DraggableCard.tsx
import {StyleSheet} from 'react-native';
import {
GestureDetector,
usePanGesture,
} from 'react-native-gesture-handler';
import Animated, {
useAnimatedStyle,
useSharedValue,
withSpring,
} from 'react-native-reanimated';

export function DraggableCard() {
const x = useSharedValue(0);
const y = useSharedValue(0);

const pan = usePanGesture({
onUpdate: event => {
x.value = event.translationX;
y.value = event.translationY;
},
onDeactivate: () => {
x.value = withSpring(0);
y.value = withSpring(0);
},
});

const animatedStyle = useAnimatedStyle(() => ({
transform: [
{translateX: x.value},
{translateY: y.value},
],
}));

return (
<GestureDetector gesture={pan}>
<Animated.View style={[styles.card, animatedStyle]} />
</GestureDetector>
);
}

const styles = StyleSheet.create({
card: {
width: 180,
height: 120,
borderRadius: 20,
backgroundColor: '#6558f5',
},
});

Use translationX and translationY when the position should be measured from the start of the current gesture. Use changeX and changeY when you need the increment since the previous event frame.

Migrate from Gesture Handler 2 to 3

The old builder API is now documented as the legacy gesture API. The most common replacements are:

Gesture Handler 2Gesture Handler 3
Gesture.Tap()useTapGesture({})
Gesture.Pan()usePanGesture({})
Gesture.Simultaneous(a, b)useSimultaneousGestures(a, b)
Gesture.Exclusive(a, b)useExclusiveGestures(a, b)
gesture.simultaneousWithExternalGesture(other)simultaneousWith: other
gesture.requireExternalGestureToFail(other)requireToFail: other
gesture.blocksExternalGesture(other)block: other
onChangeonUpdate, using changeX or changeY when needed

Do not combine relation objects from the two APIs. A safe migration sequence is:

  1. identify one connected set of gestures;
  2. convert every gesture in that set to hooks;
  3. replace the composition and cross-component relations;
  4. test activation, cancellation, and interruption on both iOS and Android;
  5. migrate the next set only after the first one is stable.

ScrollView, nested gestures, and competing interactions

Gesture relations decide which interaction can activate when a pan, scroll, swipe, or tap overlaps another gesture.

  • Use useSimultaneousGestures when both gestures should remain active.
  • Use useExclusiveGestures when the first successful gesture should win.
  • Use requireToFail when one gesture must wait for another to fail.
  • Use block when one gesture should delay another gesture's activation.

Test the real component hierarchy. A relation that works in an isolated card can behave differently inside a list, native navigator, modal, bottom sheet, or SVG tree.

Performance and accessibility checklist

  • Keep gesture callbacks small and deterministic.
  • Update shared values directly for animation-driven interactions.
  • Move to the JavaScript runtime only when React state or another JS-only API must change.
  • Test with Hermes and the React Native JavaScript Inspector rather than legacy remote JS debugging.
  • Respect reduced-motion preferences when a gesture triggers a large animation.
  • Provide a button or another accessible action when a gesture is the only way to complete an important task.
  • Test cancellation, rapid repeated input, multiple pointers, and screen-reader navigation on physical devices.

Troubleshooting

Gestures never activate

Confirm that the gesture is below GestureHandlerRootView, that the native app was rebuilt after installation, and that Gesture Handler matches the installed React Native or Expo version.

Reanimated reports a Worklets or Babel error

Install a compatible react-native-worklets version. In a Community CLI app, keep react-native-worklets/plugin last in the Babel plugins list. Then clear Metro and rebuild:

npm start -- --reset-cache

The app breaks after forcing Gesture Handler 3 in Expo

Restore the versions selected by npx expo install. Expo's bundled native modules, Expo Go, and the JavaScript packages must agree. Use a development build when your project needs a different native package version.

Gestures conflict with ScrollView or a bottom sheet

Model the relationship explicitly instead of increasing activation distances until the conflict appears to disappear. Use simultaneous, exclusive, requireToFail, or block relationships, then test both directions and interruption cases.

Where this belongs in a Dopebase project

Use the React Native dependencies guide to understand the packages and lockfiles shipped with a Dopebase app. Use this article for the general Gesture Handler setup, examples, and migration path. That separation keeps product-specific documentation stable without duplicating the public tutorial.

Next steps

Practical product notes

Get the next useful guide

Occasional tutorials, product resources, and lessons from building and launching software.