React Native Cheatsheet
Animations and Gestures
Use this React Native reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
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 |