Skip to main content

How to Use Animated View in React Native

· 4 min read
Full Stack Developer
Last updated on May 17, 2026

animated-view-react-native

Animated.View is the built-in React Native way to animate a view's opacity, transform, position, and other supported styles. It is still useful for simple microinteractions: fade-ins, scale feedback, loading motion, card entrance animations, and small transitions that do not need complex gesture logic.

For heavy gesture-driven UI, shared element transitions, and UI-thread worklets, React Native Reanimated is usually the better tool. For simple screen polish, the core Animated API is still enough.

Quick Answer

Create an Animated.Value, store it in a ref, drive it with Animated.timing, Animated.spring, Animated.sequence, or Animated.parallel, and set useNativeDriver: true when animating supported properties such as opacity and transform.

import { useEffect, useRef } from 'react';
import { Animated, Text } from 'react-native';

export function FadeInCard() {
const opacity = useRef(new Animated.Value(0)).current;

useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 240,
useNativeDriver: true,
}).start();
}, [opacity]);

return (
<Animated.View style={{ opacity }}>
<Text>Ready</Text>
</Animated.View>
);
}

If your animation depends on gestures, scroll position, or continuous UI-thread updates, check the current React Native stack and consider Reanimated.

Use a Ref for Animated Values

Do not recreate animated values on every render. Store them in useRef:

const scale = useRef(new Animated.Value(0.96)).current;

Then run animations from effects or event handlers:

function pressIn() {
Animated.spring(scale, {
toValue: 0.98,
useNativeDriver: true,
}).start();
}

function pressOut() {
Animated.spring(scale, {
toValue: 1,
useNativeDriver: true,
}).start();
}

Fade In a View

Opacity is a good fit for the native driver:

import { PropsWithChildren, useEffect, useRef } from 'react';
import { Animated, StyleProp, ViewStyle } from 'react-native';

export function FadeInView({
children,
style,
}: PropsWithChildren<{ style?: StyleProp<ViewStyle> }>) {
const opacity = useRef(new Animated.Value(0)).current;

useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 220,
useNativeDriver: true,
}).start();
}, [opacity]);

return <Animated.View style={[style, { opacity }]}>{children}</Animated.View>;
}

Use short durations for UI feedback. Long animations can make the app feel slow.

Mega Bundle Sale is ON! Get ALL of our React Native codebases at 90% OFF discount 🔥

Get the Mega Bundle

Scale a Pressable Card

transform properties also work with the native driver:

import { PropsWithChildren, useRef } from 'react';
import { Animated, Pressable } from 'react-native';

export function PressableScale({ children }: PropsWithChildren) {
const scale = useRef(new Animated.Value(1)).current;

function animate(toValue: number) {
Animated.spring(scale, {
toValue,
speed: 18,
bounciness: 4,
useNativeDriver: true,
}).start();
}

return (
<Pressable onPressIn={() => animate(0.97)} onPressOut={() => animate(1)}>
<Animated.View style={{ transform: [{ scale }] }}>
{children}
</Animated.View>
</Pressable>
);
}

This pattern works well for cards, icon buttons, product tiles, and list items.

Rotate With Interpolation

Use interpolation when the animated value needs to map to a string or derived range:

import { useEffect, useRef } from 'react';
import { Animated, Easing } from 'react-native';

export function LoadingSpinner() {
const progress = useRef(new Animated.Value(0)).current;

useEffect(() => {
const animation = Animated.loop(
Animated.timing(progress, {
toValue: 1,
duration: 900,
easing: Easing.linear,
useNativeDriver: true,
})
);

animation.start();
return () => animation.stop();
}, [progress]);

const rotate = progress.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});

return <Animated.View style={{ transform: [{ rotate }] }} />;
}

Always stop loops when the component unmounts.

Run Animations in Sequence or Parallel

Use Animated.sequence when one step should start after another:

Animated.sequence([
Animated.timing(opacity, {
toValue: 1,
duration: 180,
useNativeDriver: true,
}),
Animated.spring(scale, {
toValue: 1,
useNativeDriver: true,
}),
]).start();

Use Animated.parallel when multiple values should move together:

Animated.parallel([
Animated.timing(opacity, {
toValue: 1,
duration: 180,
useNativeDriver: true,
}),
Animated.spring(translateY, {
toValue: 0,
useNativeDriver: true,
}),
]).start();

Native Driver Rules

Use the native driver whenever possible. It sends supported animation work to native before the animation starts, so the animation can continue even if the JavaScript thread is busy.

The native driver works well with:

  • opacity;
  • transform values such as scale, translate, and rotate;
  • event-driven animations supported by Animated.event.

It does not support every style property. Layout properties such as height, width, and some color/layout changes may need another approach. If your design needs layout transitions, evaluate Reanimated or LayoutAnimation.

Looking for a custom mobile application?

Our team of expert mobile developers can help you build a custom mobile app that meets your specific needs.

Get in Touch

FAQ

Should I use Animated or Reanimated?

Use Animated for small one-off interactions. Use Reanimated for complex gestures, bottom sheets, carousels, shared values, and UI-thread animations.

Why does React Native require useNativeDriver?

React Native requires you to choose whether an animation should run on the native driver. Use true for supported properties like opacity and transforms.

Can I animate height with the native driver?

Usually no. Height is a layout property. Prefer transform-based motion or a layout animation library for that kind of transition.