Working with Gestures and Touch
1. Using Pressable Component
The Pressable component in Expo provides a flexible way to handle touch interactions and gestures. It wraps any content and allows you to define handlers for various press events.
| Property | Description | Values |
|---|---|---|
| onPress | Function called when the press is released | function |
| onLongPress | Function called when the press is held for a long time | function |
| onPressIn | Function called when the press begins | function |
| onPressOut | Function called when the press ends | function |
| android_ripple | Configuration for Android ripple effect | object |
| style | Styling for the component | style |
Example: Basic Pressable Usage
<Pressable
onPress={() => console.log("Pressed")}
onPressIn={() => console.log("Press started")}
onPressOut={() => console.log("Press ended")}
>
<Text>Tap Me</Text>
</Pressable>
Example: Pressable with Ripple Effect (Android)
<Pressable
android_ripple={{ color: "#ff0000" }}
onPress={() => console.log("Ripple effect pressed")}
>
<Text>Press with Ripple</Text>
</Pressable>
2. Handling Press Events
| Event | Description | Properties |
|---|---|---|
| onPress | Triggered when the user presses and releases the component | event object with nativeEvent |
| onLongPress | Triggered when the user presses and holds for a set duration | event object with nativeEvent |
| onPressIn | Triggered when the user presses down on the component | event object with nativeEvent |
| onPressOut | Triggered when the user releases the press on the component | event object with nativeEvent |
Example: Basic Press Event
<Pressable
onPress={() => console.log('Pressed!')}
style={({ pressed }) => [
styles.button,
{ backgroundColor: pressed ? '#ff0000' : '#00ff00' }
]}
>
<Text>Press Me</Text>
</Pressable>
Example: Long Press Event
<Pressable
onLongPress={() => console.log('Long pressed!')}
style={styles.button}
>
<Text>Press and Hold</Text>
</Pressable>
Note:
Pressable is the recommended component for handling press events in Expo, as it provides better accessibility and gesture support than TouchableOpacity.
3. Using Gesture Handler
Example: Basic Tap Gesture
import { TouchableOpacity } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
const tapGesture = Gesture.Tap()
.onEnd((event) => {
console.log('Tap recognized');
});
export default function App() {
return (
<GestureDetector gesture={tapGesture}>
<TouchableOpacity style={{ flex: 1 }}>
<Text>Tap me</Text>
</TouchableOpacity>
</GestureDetector>
);
}
| Gesture | Description | Properties |
|---|---|---|
| Gesture.Tap | Recognizes a tap gesture | numberOfTaps, tappingThreshold |
| Gesture.Pan | Recognizes pan (drag) gestures | minDistance, activeOffsetX, activeOffsetY |
| Gesture.LongPress | Recognizes long press gestures | minDuration, maxDistance |
| Gesture.Simultaneous | Allows multiple gestures to be recognized simultaneously | gestures |
| Gesture.Exclusive | Ensures only one gesture is recognized at a time | gestures |
Note: Gesture Handler requires native modules and must be properly linked in your Expo project.
Warning: On Android, you must add
android:hardwareAccelerated="true" to your AndroidManifest.xml.
Gesture Handler Setup
- Install
react-native-gesture-handler - Add
<GestureHandlerRootView>at the root of your app - Link native modules (via Expo)
4. Implementing Pan Gestures
| Property | Description | Values |
|---|---|---|
| onPanGestureEvent | Callback function triggered during pan gesture | function |
| onPanStateChange | Callback function triggered when pan state changes | function |
| activeOffsetX | Active offset for X axis | number |
| activeOffsetY | Active offset for Y axis | number |
| failOffsetX | Fail offset for X axis | number |
| failOffsetY | Fail offset for Y axis | number |
Example: Basic Pan Gesture
import { PanGestureHandler } from 'react-native-gesture-handler';
PanGestureHandler
.onPanGestureEvent((event) => {
console.log('Pan offset X: ', event.nativeEvent.translationX);
console.log('Pan offset Y: ', event.nativeEvent.translationY);
})
.onPanStateChange((event) => {
if (event.nativeEvent.state === 1) {
console.log('Pan started');
} else if (event.nativeEvent.state === 3) {
console.log('Pan ended');
}
});
5. Using Tap Gestures
| Property | Description | Values |
|---|---|---|
| onPress | Callback function when the touch is released | function |
| onLongPress | Callback function when the touch is held for a long time | function |
| onPressIn | Callback function when the touch begins | function |
| onPressOut | Callback function when the touch ends | function |
Example: Basic Tap Gesture
<TouchableOpacity onPress='() => console.log("Tapped")'>
<Text>Press Me</Text>
</TouchableOpacity>
Example: Long Press Gesture
<TouchableOpacity onLongPress='() => console.log("Long Pressed")'>
<Text>Press and Hold</Text>
</TouchableOpacity>
6. Implementing Swipe Gestures
| Property | Description | Values |
|---|---|---|
| swipeDirection | Direction of the swipe gesture | "left", "right", "up", "down" |
| threshold | Minimum distance in pixels to recognize a swipe | 50 |
| velocityThreshold | Minimum velocity in pixels per second to recognize a swipe | 1000 |
Example: Swipe Gesture Handler
import { GestureDetector } from 'react-native-gesture-handler';<br><br>const SwipeExample = () => {<br> const swipeHandler = Gesture.Swipe()<br> .setMinDistance(50)<br> .setVelocityThreshold(1000)<br> .onEnd((event) => {<br> if (event.velocityX < 0) {<br> console.log('Swiped left');<br> } else {<br> console.log('Swiped right');<br> }<br> });<br><br> return (<br> <GestureDetector gesture={swipeHandler}><br> <View style={{ width: 200, height: 200, backgroundColor: 'lightblue' }} /><br> </GestureDetector><br> );<br>};
Note: The
react-native-gesture-handler library is required for swipe gestures.
7. Using Pinch Gestures
| Property | Description | Values |
|---|---|---|
| pinchEnabled | Enables or disables pinch gesture handling | true | false |
| onPinchEvent | Callback function triggered on pinch gesture changes | Function with event parameter |
| pinchSensitivity | Sensitivity of the pinch gesture recognition | number (0.1 - 1.0) |
Example: Basic Pinch Gesture Setup
import { PinchGestureHandler } from 'react-native-gesture-handler';
const handlePinch = (event) => {
console.log('Scale:', event.scale);
};
<PinchGestureHandler
onGestureEvent={handlePinch}
pinchSensitivity={0.5}
>
<View style={{ width: 200, height: 200 }}>
<Image source={require('./image.jpg')} style={{ flex: 1 }} />
</View>
</PinchGestureHandler>
Note: The
pinchSensitivity property allows fine-tuning of how sensitive the gesture handler is to pinch movements.
Warning: Ensure that the view being pinched has a defined size or layout constraints to prevent unexpected behavior.
8. Creating Rotation Gestures
| Property | Description | Values |
|---|---|---|
| rotation | Rotation angle in degrees | number |
| originX | Horizontal origin point for rotation | number |
| originY | Vertical origin point for rotation | number |
Example: Basic Rotation Gesture
import { Gesture } from 'react-native-gesture-handler';
const rotationGesture = Gesture.Rotation()
.onUpdate((event) => {
console.log('Rotation:', event.rotation);
});
export default function RotationExample() {
return (
<View style={styles.container}>
<GestureHandlerRootView style={styles.container}>
<TouchableOpacity
style={styles.box}
onPress={rotationGesture}
>
<Text>Rotate Me</Text>
</TouchableOpacity>
</GestureHandlerRootView>
</View>
);
}
Note: The rotation gesture can be combined with other gestures for complex interactions.
9. Combining Gestures
Example: Combining Tap and Long Press
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
const tap = Gesture.Tap().numberOfTaps(1);
const longPress = Gesture.LongPress().minDuration(500);
const combinedGesture = Gesture.Exclusive(tap, longPress);
export default function CombinedGestures() {
return (
<GestureDetector gesture={combinedGesture}>
<View style={styles.box} />
</GestureDetector>
);
}
| Gesture | Description | Usage |
|---|---|---|
| Gesture.Exclusive() | Only one gesture is recognized at a time | Use for mutually exclusive gestures |
| Gesture.Simultaneous() | Gestures can be recognized at the same time | Use for overlapping gestures like pan + tap |
| Gesture.Race() | First gesture to start is recognized | Use for fallbacks or alternatives |
Note: Exclusive gestures prevent conflicts, while simultaneous gestures allow multiple interactions to occur at once.
Gesture Combination Strategies
- Exclusive: One gesture at a time, no overlap
- Simultaneous: All gestures can fire together
- Race: First gesture wins, others ignored
10. Using Touch Events
| Event Type | Description | Properties |
|---|---|---|
| onTouchStart | Triggered when a touch begins on the element | nativeEvent, touches |
| onTouchMove | Triggered when a touch moves on the element | nativeEvent, touches |
| onTouchEnd | Triggered when a touch ends on the element | nativeEvent, touches |
Example: Basic Touch Event Handler
import React from 'react';
import { View } from 'react-native';
const TouchHandler = () => {
const handleTouchStart = (e) => {
console.log('Touch started:', e.nativeEvent.touches);
};
return (
<View
onTouchStart={handleTouchStart}
style={{ width: 200, height: 200, backgroundColor: 'lightblue' }}
>
<Text>Touch me</Text>
</View>
);
};
export default TouchHandler;
Note: Touch events in Expo are handled similarly to React Native, but with additional support for gesture handling via
react-native-gesture-handler.
11. Creating Gesture Composables
| Property | Description | Example |
|---|---|---|
| useGesture | Hook to create custom gesture handlers | const gesture = useGesture(() => ({ |
| onStart | Callback when gesture starts | onStart: (event) => console.log('Started') |
| onMove | Callback when gesture moves | onMove: (event) => console.log('Moved') |
| onEnd | Callback when gesture ends | onEnd: (event) => console.log('Ended') |
Example: Basic Gesture Composable
import { useGesture } from 'react-native-gesture-handler';\n\nconst MyGesture = () => {\n const gesture = useGesture({\n onStart: (event) => console.log('Started'),\n onMove: (event) => console.log('Moved'),\n onEnd: (event) => console.log('Ended')\n });\n\n return (\n <TouchableOpacity gesture={gesture}>\n <Text>Press Me</Text>\n </TouchableOpacity>\n );\n};
Note: Gesture composable hooks should be used inside components that support gesture handling, such as
TouchableOpacity or View with gesture prop.
Warning: Make sure to properly handle gesture state transitions to avoid unexpected behavior or performance issues.
12. Handling Double Tap
| Property | Description | Values |
|---|---|---|
| onDoubleTap | Callback function triggered when a double tap is detected | function |
Example: Double Tap Handler
import { TouchableOpacity } from 'react-native';
<TouchableOpacity
onDoubleTap={() => console.log('Double tap detected')}
style={styles.button}
>
<Text>Double Tap Me</Text>
</TouchableOpacity>
Note: The
onDoubleTap property is not part of the standard React Native API. It's a custom implementation or provided by libraries like react-native-gesture-handler.