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'

Shared Values & Animated Styles

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
  withSpring,
} from 'react-native-reanimated';

function Box() {
  const offset = useSharedValue(0);      // mutable, lives on the UI thread
  const opacity = useSharedValue(1);

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

  const move = () => {
    offset.value = withSpring(100);      // assign → animates
    opacity.value = withTiming(0.5, { duration: 300 });
  };

  return <Animated.View style={[styles.box, animatedStyle]} />;
}

Read/write shared values via .value. Never destructure them or read .value during render — read inside worklets (useAnimatedStyle, gesture callbacks) or event handlers.

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 }} />
UseReach for
Simple fade/slide, no depsBuilt-in Animated
Gesture-driven, layout, colors, 60fps under JS loadReanimated + gesture-handler
Mount/unmount/list reorderReanimated entering/exiting/layout