React Native Cheatsheet
Animations and Gestures
Use this React Native reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Reanimated Setup
Reanimated runs animation "worklets" on the UI thread — no bridge hops, 60fps even while JS is busy. Reanimated 3 works on both architectures; Reanimated 4 requires the New Architecture (default since RN 0.76) and keeps the same shared-value API.
npx expo install react-native-reanimated
# or: npm install react-native-reanimated// babel.config.js — bare RN with Reanimated 3 (Expo's preset configures this) module.exports = { presets: ['module:@react-native/babel-preset'], plugins: ['react-native-reanimated/plugin'], // MUST be last }; // Reanimated 4 moves the plugin to 'react-native-worklets/plugin'
Animation Functions
import { withTiming, withSpring, withDelay, withSequence, withRepeat, Easing, cancelAnimation, } from 'react-native-reanimated'; // Timing with easing sv.value = withTiming(100, { duration: 500, easing: Easing.out(Easing.quad), }); // Physics-based spring sv.value = withSpring(100, { damping: 15, stiffness: 120 }); // Delay, then animate sv.value = withDelay(250, withTiming(1)); // Run animations one after another sv.value = withSequence( withTiming(1.2, { duration: 100 }), withTiming(1, { duration: 100 }) ); // Repeat (numberOfReps, reverse) — -1 = infinite sv.value = withRepeat(withTiming(360, { duration: 1000 }), -1, false); // Stop an in-flight animation cancelAnimation(sv); // Completion callback (runs on the UI thread) sv.value = withTiming(0, { duration: 200 }, (finished) => { 'worklet'; if (finished) runOnJS(onClosed)(); });
Entering, Exiting & Layout Animations
Declarative mount/unmount/reorder animations — no shared values needed.
import Animated, { FadeIn, FadeOut, SlideInRight, SlideOutLeft, ZoomIn, LinearTransition, } from 'react-native-reanimated'; <Animated.View entering={FadeIn.duration(300).delay(100)} exiting={SlideOutLeft.springify()} layout={LinearTransition} // animates position when siblings change /> // In lists — each row animates in and reflows smoothly on delete {items.map(item => ( <Animated.View key={item.id} entering={ZoomIn} exiting={FadeOut} layout={LinearTransition}> <Row item={item} /> </Animated.View> ))}
Scroll-Driven Animation & interpolate
import Animated, { useSharedValue, useAnimatedScrollHandler, useAnimatedStyle, interpolate, Extrapolation, } from 'react-native-reanimated'; const scrollY = useSharedValue(0); const onScroll = useAnimatedScrollHandler((e) => { scrollY.value = e.contentOffset.y; }); const headerStyle = useAnimatedStyle(() => ({ height: interpolate(scrollY.value, [0, 120], [200, 80], Extrapolation.CLAMP), opacity: interpolate(scrollY.value, [0, 120], [1, 0.6]), })); <Animated.ScrollView onScroll={onScroll} scrollEventThrottle={16}>...</Animated.ScrollView> <Animated.View style={[styles.header, headerStyle]} />
// Calling back into JS from a worklet import { runOnJS } from 'react-native-reanimated'; const gesture = Gesture.Tap().onEnd(() => { runOnJS(navigateToDetails)(itemId); // JS functions must go through runOnJS });
Gesture Handler Setup
npx expo install react-native-gesture-handler
# or: npm install react-native-gesture-handler// App root — required once import { GestureHandlerRootView } from 'react-native-gesture-handler'; export default function App() { return ( <GestureHandlerRootView style={{ flex: 1 }}> <Navigation /> </GestureHandlerRootView> ); }
Gestures (Gesture API v2)
import { Gesture, GestureDetector } from 'react-native-gesture-handler'; // Tap / double tap / long press const tap = Gesture.Tap().onEnd(() => runOnJS(onTap)()); const doubleTap = Gesture.Tap().numberOfTaps(2).onEnd(() => runOnJS(onLike)()); const longPress = Gesture.LongPress().minDuration(500).onStart(() => runOnJS(openMenu)()); // Pinch to zoom const scale = useSharedValue(1); const savedScale = useSharedValue(1); const pinch = Gesture.Pinch() .onUpdate((e) => { scale.value = savedScale.value * e.scale; }) .onEnd(() => { savedScale.value = scale.value; }); // Composition const composed = Gesture.Exclusive(doubleTap, tap); // doubleTap wins // Gesture.Simultaneous(pinch, pan) — run together // Gesture.Race(pan, longPress) — first to activate wins <GestureDetector gesture={composed}> <Animated.View style={animatedStyle} /> </GestureDetector>
Drag Card — Gestures + Reanimated Together
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; function DraggableCard() { const x = useSharedValue(0); const y = useSharedValue(0); const start = useSharedValue({ x: 0, y: 0 }); const pan = Gesture.Pan() .onStart(() => { start.value = { x: x.value, y: y.value }; }) .onUpdate((e) => { x.value = start.value.x + e.translationX; y.value = start.value.y + e.translationY; }) .onEnd(() => { x.value = withSpring(0); // snap back y.value = withSpring(0); }); const style = useAnimatedStyle(() => ({ transform: [{ translateX: x.value }, { translateY: y.value }], })); return ( <GestureDetector gesture={pan}> <Animated.View style={[styles.card, style]} /> </GestureDetector> ); }
Built-in Animated API (no dependency)
Fine for simple fades/slides; only opacity and transform run natively.
import { Animated, useAnimatedValue } from 'react-native'; const opacity = useAnimatedValue(0); // RN 0.71+ hook Animated.timing(opacity, { toValue: 1, duration: 300, useNativeDriver: true, // always, when animating opacity/transform }).start(); <Animated.View style={{ opacity }} />
| Use | Reach for |
|---|---|
| Simple fade/slide, no deps | Built-in Animated |
| Gesture-driven, layout, colors, 60fps under JS load | Reanimated + gesture-handler |
| Mount/unmount/list reorder | Reanimated entering/exiting/layout |