Smooth motion is what makes an app feel native. Animate with the built-in Animated API and Reanimated, and respond to drags and swipes with the gesture handler.
Why: Animated ships with React Native and covers the common cases — fades, slides, scaling. You create an Animated.Value, drive it with Animated.timing, and bind it to a style on an Animated.View. useNativeDriver runs the animation on the UI thread so it stays smooth even when JS is busy.
import { useRef, useEffect } from 'react'
import { Animated } from 'react-native'
function FadeIn({ children }) {
const opacity = useRef(new Animated.Value(0)).current
useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 400,
useNativeDriver: true, // run on the UI thread
}).start()
}, [])
return <Animated.View style={{ opacity }}>{children}</Animated.View>
}Why: for gesture-driven, interruptible, 60fps animations, Reanimated is the modern standard — it runs your animation logic entirely on the UI thread. useSharedValue holds the animated value and useAnimatedStyle maps it to styles. Spring physics give natural motion.
// npx expo install react-native-reanimated
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated'
function Bouncer() {
const scale = useSharedValue(1)
const style = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }] }))
return (
<Animated.View style={[{ width: 80, height: 80 }, style]} />
// call: scale.value = withSpring(1.5) to animate
)
}Why: taps are handled by Pressable, but drags, swipes, pinches, and long-presses need the gesture handler. react-native-gesture-handler gives you composable gestures that integrate with Reanimated for buttery interactions like swipe-to-delete or a draggable card.
// npx expo install react-native-gesture-handler
import { GestureDetector, Gesture } from 'react-native-gesture-handler'
const pan = Gesture.Pan().onUpdate((e) => {
// e.translationX / e.translationY — move something with the finger
})
function Draggable({ children }) {
return <GestureDetector gesture={pan}>{children}</GestureDetector>
}