Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build performant React Native and Expo apps with best practices for lists, animations, and navigation
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/animation-gesture-detector-press.md
1---2title: Use GestureDetector for Animated Press States3impact: MEDIUM4impactDescription: UI thread animations, smoother press feedback5tags: animation, gestures, press, reanimated6---78## Use GestureDetector for Animated Press States910For animated press states (scale, opacity on press), use `GestureDetector` with11`Gesture.Tap()` and shared values instead of Pressable's12`onPressIn`/`onPressOut`. Gesture callbacks run on the UI thread as worklets—no13JS thread round-trip for press animations.1415**Incorrect (Pressable with JS thread callbacks):**1617```tsx18import { Pressable } from 'react-native'19import Animated, {20useSharedValue,21useAnimatedStyle,22withTiming,23} from 'react-native-reanimated'2425function AnimatedButton({ onPress }: { onPress: () => void }) {26const scale = useSharedValue(1)2728const animatedStyle = useAnimatedStyle(() => ({29transform: [{ scale: scale.value }],30}))3132return (33<Pressable34onPress={onPress}35onPressIn={() => (scale.value = withTiming(0.95))}36onPressOut={() => (scale.value = withTiming(1))}37>38<Animated.View style={animatedStyle}>39<Text>Press me</Text>40</Animated.View>41</Pressable>42)43}44```4546**Correct (GestureDetector with UI thread worklets):**4748```tsx49import { Gesture, GestureDetector } from 'react-native-gesture-handler'50import Animated, {51useSharedValue,52useAnimatedStyle,53withTiming,54interpolate,55runOnJS,56} from 'react-native-reanimated'5758function AnimatedButton({ onPress }: { onPress: () => void }) {59// Store the press STATE (0 = not pressed, 1 = pressed)60const pressed = useSharedValue(0)6162const tap = Gesture.Tap()63.onBegin(() => {64pressed.set(withTiming(1))65})66.onFinalize(() => {67pressed.set(withTiming(0))68})69.onEnd(() => {70runOnJS(onPress)()71})7273// Derive visual values from the state74const animatedStyle = useAnimatedStyle(() => ({75transform: [76{ scale: interpolate(withTiming(pressed.get()), [0, 1], [1, 0.95]) },77],78}))7980return (81<GestureDetector gesture={tap}>82<Animated.View style={animatedStyle}>83<Text>Press me</Text>84</Animated.View>85</GestureDetector>86)87}88```8990Store the press **state** (0 or 1), then derive the scale via `interpolate`.91This keeps the shared value as ground truth. Use `runOnJS` to call JS functions92from worklets. Use `.set()` and `.get()` for React Compiler compatibility.9394Reference:95[Gesture Handler Tap Gesture](https://docs.swmansion.com/react-native-gesture-handler/docs/gestures/tap-gesture)96