Animated API — The Foundation
React Native's built-in Animated API runs animations on the JS thread by default, but with `useNativeDriver: true` the animation values are transferred to the UI thread and run at 60fps regardless of JS work. Understanding this is the foundation of smooth animations.
Core Animated Patterns
import { Animated, Easing, StyleSheet, Pressable, Text } from 'react-native';
import { useRef, useEffect } from 'react';
// ---- Fade In on mount ----
function FadeInView({ children }: { children: React.ReactNode }) {
const opacity = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 300,
easing: Easing.out(Easing.cubic),
useNativeDriver: true, // ← ALWAYS use this for opacity/transform
}).start();
}, []);
return <Animated.View style={{ opacity }}>{children}</Animated.View>;
}
// ---- Spring — feels physical ----
function SpringButton({ label }: { label: string }) {
const scale = useRef(new Animated.Value(1)).current;
function onPressIn() {
Animated.spring(scale, {
toValue: 0.94,
useNativeDriver: true,
speed: 50,
bounciness: 4,
}).start();
}
function onPressOut() {
Animated.spring(scale, {
toValue: 1,
useNativeDriver: true,
speed: 50,
bounciness: 4,
}).start();
}
return (
<Pressable onPressIn={onPressIn} onPressOut={onPressOut}>
<Animated.View style={[styles.btn, { transform: [{ scale }] }]}>
<Text style={styles.btnText}>{label}</Text>
</Animated.View>
</Pressable>
);
}
// ---- Sequence & Parallel ----
function AnimatedLogo() {
const scale = useRef(new Animated.Value(0)).current;
const opacity = useRef(new Animated.Value(0)).current;
const translateY = useRef(new Animated.Value(20)).current;
useEffect(() => {
Animated.sequence([
// Start by fading & sliding up together
Animated.parallel([
Animated.timing(opacity, { toValue: 1, duration: 400, useNativeDriver: true }),
Animated.timing(translateY, { toValue: 0, duration: 400, useNativeDriver: true, easing: Easing.out(Easing.back(1.2)) }),
]),
// Then bounce in scale
Animated.spring(scale, { toValue: 1, useNativeDriver: true, bounciness: 12 }),
]).start();
}, []);
return (
<Animated.View style={{ opacity, transform: [{ translateY }, { scale }] }}>
<Text style={{ fontSize: 48 }}>🚀</Text>
</Animated.View>
);
}
// ---- Loop animation (pulsing badge) ----
function PulsingDot() {
const scale = useRef(new Animated.Value(1)).current;
useEffect(() => {
const pulse = Animated.loop(
Animated.sequence([
Animated.timing(scale, { toValue: 1.3, duration: 800, useNativeDriver: true, easing: Easing.inOut(Easing.sine) }),
Animated.timing(scale, { toValue: 1, duration: 800, useNativeDriver: true, easing: Easing.inOut(Easing.sine) }),
])
);
pulse.start();
return () => pulse.stop();
}, []);
return <Animated.View style={[styles.dot, { transform: [{ scale }] }]} />;
}
const styles = StyleSheet.create({
btn: { backgroundColor: '#6366f1', padding: 16, borderRadius: 12, alignItems: 'center' },
btnText: { color: 'white', fontWeight: '700', fontSize: 16 },
dot: { width: 12, height: 12, borderRadius: 6, backgroundColor: '#4ade80' },
});Tip
Tip
Practice Animated API The Foundation in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
React Native bridges JavaScript and native platform code
Practice Task
Note
Practice Task — (1) Write a working example of Animated API The Foundation from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Animated API The Foundation is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready react native code.