Animation basics
An animation is a smooth change of a style over time: opacity, position, size. On a phone it shows what happened: a new task smoothly appeared, a deleted one faded out. React Native has the built-in Animated module, and for complex cases, the Reanimated library.
Animated: fading in
Let us make a wrapper that fades its content in over 400 milliseconds:
// components/FadeIn.tsx
import { type ReactNode, useEffect, useRef } from 'react';
import { Animated } from 'react-native';
export default function FadeIn({ children }: { children: ReactNode }) {
const opacity = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 400,
useNativeDriver: true,
}).start();
}, [opacity]);
return <Animated.View style={{ opacity }}>{children}</Animated.View>;
}How it works:
Animated.Value(0)is an animatable number with an initial opacity of 0.useRefkeeps it between renders.Animated.Viewis aViewthat understands such values instyle.Animated.timingchanges the value totoValueoverdurationmilliseconds, andstart()runs the animation.useNativeDriver: truehands the animation to native code, so it stays smooth even when JavaScript is busy.
Using it
Wrap the task row in the list's renderItem:
// app/index.tsx
import FadeIn from '@/components/FadeIn';
renderItem={({ item }) => (
<FadeIn>
<TaskItem task={item} onToggle={toggleTask} />
</FadeIn>
)}Add a task: it does not pop up abruptly but fades in. Tasks already on the screen do not flicker, because the animation runs only when a component is shown for the first time.
What useNativeDriver can animate
| Yes | No |
|---|---|
opacity | width, height |
transform: move, scale, rotate | backgroundColor |
margin, padding |
For layout properties useNativeDriver is set to false, but such an animation is less smooth.
Reanimated
The react-native-reanimated library is already installed in the Expo template. It runs animations entirely on the native thread and offers ready-made enter and exit animations:
// components/TaskItem.tsx
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
<Animated.View entering={FadeIn} exiting={FadeOut}>
<Text>{task.title}</Text>
</Animated.View>With entering and exiting, a task fades in when added and fades out when deleted, without manual values or effects. For manually controlled animations, Reanimated has the useSharedValue and useAnimatedStyle hooks and the withTiming function.
Common mistake
Creating an Animated.Value right in the component body: const opacity = new Animated.Value(0);. Every render creates a new value equal to zero, and the animation resets or does not run at all. Keep it in useRef, as in the example.