Animations Best Practices
A condensed summary of the 25 most important best practices drawn from every page in this section.
Search across all documentation pages
A condensed summary of the 25 most important best practices drawn from every page in this section.
Install with npx expo install: react-native-reanimated and react-native-gesture-handler versions must match the SDK 57 matrix - manual npm pins cause native build failures.
Babel plugin last: react-native-reanimated/plugin is always the final entry in babel.config.js - other plugins after it break worklet transforms.
Clear Metro after plugin changes: Run npx expo start --clear whenever Babel or Reanimated versions change - stale caches produce "failed to create a worklet" errors.
Wrap root with GestureHandlerRootView: Required in app/_layout.tsx for pans, drawers, and stack-adjacent gestures - no compile-time warning when missing.
Import Animated from Reanimated for worklets: react-native-reanimated's Animated supports useAnimatedStyle, entering/exiting, and shared transitions - not react-native's Animated.
Use shared values for interactive motion: useSharedValue updates do not re-render React - the foundation of 60fps drags, springs, and scroll-linked styles.
Never setState every animation frame: onPanResponderMove or gesture onUpdate calling setState competes with React reconciliation - dropped frames on any mid-tier device.
Prefer Reanimated for gesture-linked UI: If motion follows a finger or runs during list scroll, graduate from the legacy Animated API to Reanimated 4.
Keep Animated for simple one-shot fades: Short opacity timing on static mount with useNativeDriver: true is acceptable - do not over-engineer toast fades.
Know the native driver boundary: opacity and transform support useNativeDriver: true; width, height, and flex need Reanimated layout animations or useAnimatedStyle.
Pair RNGH with Reanimated: Gesture recognition on the native thread plus shared-value updates on the UI thread - Pressable alone cannot match pan frequency.
Tune pans before rewriting scroll trees: activeOffsetX and failOffsetY fix most swipe-row vs ScrollView conflicts - see Gesture Conflict Resolution.
Use simultaneousWithExternalGesture for swipe rows: Wire the parent ScrollView / FlatList ref so horizontal row pans coexist with vertical scroll after offset qualification.
Import inner scrollables from RNGH when nested scroll fails: Switch the inner horizontal FlatList first - not both axes blindly (Nested Scrollables).
Stable keys for animated lists: keyExtractor must return server ids - index keys make layout animations and shared transition tags jump between wrong rows.
One entering/exiting wrapper per row: Apply FadeIn / SlideInRight and layout={Layout} on a single Animated.View per renderItem - not on every nested Text.
Shorten exiting duration: Exiting at 150–220ms feels snappier than symmetric 400ms enter/exit - users perceive delete feedback faster.
Cap stagger delays on long lists: FadeInDown.delay(index * 40) beyond ~12–20 items adds noticeable mount latency - stagger only the first viewport.
Unique sharedTransitionTag per entity: Include stable ids in tag strings - duplicate tags across list rows break hero transitions (Shared Element Transitions).
Match tags exactly across stack screens: List cell and Expo Router detail route must use the same tag builder - pair with nested stacks (Stack Navigation).
runOnJS sparingly: Bridge to navigation, setState, and analytics on gesture end - not inside onUpdate every frame.
Extract shared worklet helpers: Clamp, rubber-band, and interpolate functions belong in 'worklet' modules - reduces Hermes duplication and eases testing (Worklets & Bundle Mode).
Profile release on mid-tier Android: iPhone Pro simulators and Expo Go on Mac hide JS-thread contention - Flashlight or RN DevTools on a low-RAM device before ship.
Respect reduced motion: Check AccessibilityInfo.isReduceMotionEnabled() and skip or shorten entering/layout animations - provide instant state transitions as fallback.
Run the gesture checklist before release: Walk swipe-in-scroll, nested rails, sheet-over-feed, shared hero pop, and fast-list delete on Android - simulators miss scroll-vs-swipe races.
Animated remains fine for rare static fades with useNativeDriver: true.FadeIn, Layout) does not require RNGH.Stack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026