rn-animations-gestures — guardrail for animations + gestures in RN/Expo
For the current Expo API and per-version details, verify against the Expo docs / MCP
mcp.expo.dev/expo/skills(see rn-fundamentals → Source of truth).
The 5 rules (non-negotiable)
- Reanimated 4 is the default. Never the legacy
AnimatedAPI fromreact-nativefor new code. Reanimated 4 also ships a web-style CSS Animations/Transitions API (transition: {...},animationNamekeyframes) as a backward-compatible ADDITION to worklets — good for state-driven style changes, not a replacement foruseSharedValue/useAnimatedStyleon gesture-driven motion (seereferences/patterns.md). - Gestures via the Gesture Handler
GestureAPI. NeverPanResponder. Do not go looking for a major number: npm'slatestis 3.x, and Expo SDK 57 bundles~2.32.0—expo installwill not give you 3, and theGestureAPI below is the same in both. Installing what npm says here is the exact mistakern-bootstrap/references/stack-defaults.mddocuments. UseGesture.Pan(),Gesture.Pinch(),Gesture.Tap(),Gesture.LongPress()with<GestureDetector>. - Worklets run on the UI thread — they CANNOT access React state directly. To call back to JS use
runOnJS(fn)(args). Inside worklets, only shared values, locals, andrunOnUI/runOnJSare safe. - Layout animations for enter/exit/move. Use
entering={FadeIn},exiting={FadeOut},layout={LinearTransition.springify()}fromreact-native-reanimated— they handle their own worklets correctly. useDerivedValuefor computed shared values. NeveruseMemoon a shared value —useMemoruns on JS thread.
Quick decision tree
- "What's the right tool — worklets or the CSS Animations/Transitions API?" →
references/decision-tree.md - "How do I structure a worklet-driven animation?" →
references/patterns.md - "What native module setup do I need?" → Reanimated 4,
react-native-workletsand Gesture Handler are all inrn-bootstrap'sinstall-stack.sh. No babel config to write:babel-preset-expowires the worklets plugin when the library is installed.
Common anti-patterns (NEVER do)
- ❌
import { Animated } from "react-native"— usereact-native-reanimated'sAnimatedinstead. - ❌
setMyState(newValue)from inside a worklet — wrap inrunOnJS(setMyState)(newValue). - ❌ Reading
props.foodirectly insideuseAnimatedStyle— capture into a shared value first. - ❌
PanResponder— deprecated for new code. Use<GestureDetector>. - ❌ Installing
react-native-reanimatedalone. Reanimated 4 moved worklets intoreact-native-worklets, a separate required package — Expo's own line isnpx expo install react-native-reanimated react-native-worklets. It builds without it and fails at runtime. - ❌ Animating
height: 'auto'— impossible on the UI thread (no measure). Either measure withonLayoutor useLayoutAnimationfrom Reanimated. - ❌ Chaining
withTiming(...)insideuseEffectwithoutcancelAnimation— leaks on unmount.
Sources
- Course: codewithbeto.dev/rnCourse — "Animations & Gestures" module (paid, distilled).
- Official: https://docs.swmansion.com/react-native-reanimated/docs/
- Official: https://docs.swmansion.com/react-native-gesture-handler/docs/