DEV Community

Cover image for Mastering UI Animations in React Native Using Reanimated — A Practical Guide
Saloni Agrawal
Saloni Agrawal

Posted on

Mastering UI Animations in React Native Using Reanimated — A Practical Guide

Animations are often treated as an optional enhancement in mobile apps. Many developers open a Figma file, inspect layout spacing, apply colors, build screens, and move on — without ever thinking about motion.

But animations are not decoration. They are communication. They guide attention, improve perceived performance, and make the UI feel natural. Without them, interfaces feel abrupt and lifeless.

There are multiple animation libraries in the React Native ecosystem — the built-in Animated API, Moti, LayoutAnimation, and Reanimated. Each works differently and serves different use cases, but in this guide, we’ll focus specifically on the Reanimated library.


Why Developers Should Care About Animations

Animations improve the UX in four major ways:

  1. They explain UI changes
  2. They reduce perceived waiting time
  3. They make transitions smoother and more intuitive
  4. They create a premium experience that keeps users engaged

Too much animation can slow down devices, especially low-end Android phones. Good animation is subtle, intentional, and efficient.


Why Reanimated?

Reanimated is preferred for modern animation work because it runs animations on the UI thread, not the JavaScript thread.


UI Thread vs JS Thread

┌──────────────────────────┐      ┌───────────────────────────┐
│      JavaScript Thread   │      │         UI Thread         │
│--------------------------│      │---------------------------│
│ Runs business logic      │      │ Draws UI elements         │
│ API calls                │      │ Handles layout & paint    │
│ React state updates      │      │ Runs Reanimated frames    │
│ Event handlers           │      │ 60fps rendering           │
└──────────────────────────┘      └───────────────────────────┘

If animations run here → ❌ can stutter (JS thread can get busy)

If animations run here → ✅ smooth, stable, 60fps  
Enter fullscreen mode Exit fullscreen mode

This separation between threads is the foundation of Reanimated’s performance model.


1. What an Animation Represents

An animation is simply a value changing over time:

  • opacity: 0 → 1
  • position: 20px → 0px
  • scale: 0.9 → 1

2. Shared Values (useSharedValue — Hook)

Shared values are the foundation of Reanimated.

import { useSharedValue } from 'react-native-reanimated';

const opacity = useSharedValue(0.5);
Enter fullscreen mode Exit fullscreen mode

Shared values:

  • Live on the UI thread
  • Update at 60fps
  • Do NOT trigger React re-renders
  • Drive animations without JS slowdowns

They’re like animation‑friendly state variables.


3. Timing Animations (withTiming — Animation Function)

opacity.value = withTiming(1, { duration: 400 });
Enter fullscreen mode Exit fullscreen mode

Meaning:

“Animate opacity from current value → 1 in 400ms.”

Used for:

  • fades
  • sliding transitions
  • smooth, predictable motion

4. Spring Animations (withSpring — Animation Function)

scale.value = withSpring(1, { damping: 8, stiffness: 100 });
Enter fullscreen mode Exit fullscreen mode

This creates:

  • bounce
  • natural easing
  • gesture‑friendly interactions

Perfect for:

  • button presses
  • cards
  • playful UI

5. Animated Styles (useAnimatedStyle — Hook)

useAnimatedStyle takes your shared values and turns them into renderable UI styles that the native thread can animate smoothly.

const animatedStyle = useAnimatedStyle(() => ({
  opacity: opacity.value,
  transform: [
    { translateY: translateY.value },
    { scale: scale.value }
  ],
}));
Enter fullscreen mode Exit fullscreen mode

Runs on the UI thread and updates without React re-renders.


Putting It All Together

Now that we understand each piece, here’s how they form a complete animation workflow.

Step 1: Create Shared Values

const opacity = useSharedValue(0);
const translateY = useSharedValue(20);
const scale = useSharedValue(0.9);
Enter fullscreen mode Exit fullscreen mode

Step 2: Trigger Animations

useEffect(() => {
  opacity.value = withTiming(1, { duration: 400 });
  translateY.value = withTiming(0, { duration: 400 });
  scale.value = withSpring(1);
}, []);
Enter fullscreen mode Exit fullscreen mode

Step 3: Create Animated Styles

const animatedStyle = useAnimatedStyle(() => ({
  opacity: opacity.value,
  transform: [
    { translateY: translateY.value },
    { scale: scale.value }
  ],
}));
Enter fullscreen mode Exit fullscreen mode

Step 4: Apply to UI

<Animated.View style={animatedStyle}>
  <Text style={{ fontSize: 28, fontWeight: '600' }}>
    Design.
  </Text>
</Animated.View>
Enter fullscreen mode Exit fullscreen mode

Animated.View is required for animated styles.


Working Example

Animation Demo


Complete Code Example

import React, { useEffect } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import Animated, { 
  useSharedValue,
  useAnimatedStyle,
  withTiming,
  withSpring
} from 'react-native-reanimated';

export default function AnimatedWordCard() {
  const opacity = useSharedValue(0);
  const translateY = useSharedValue(20);
  const scale = useSharedValue(0.9);

  useEffect(() => {
    opacity.value = withTiming(1, { duration: 400 });
    translateY.value = withTiming(0, { duration: 400 });
    scale.value = withSpring(1);
  }, []);

  const animatedStyle = useAnimatedStyle(() => ({
    opacity: opacity.value,
    transform: [
      { translateY: translateY.value },
      { scale: scale.value }
    ],
  }));

  return (
    <View style={{
      flex: 1,
      backgroundColor: "#0D0D0D",
      justifyContent: "center",
      alignItems: "center",
      paddingHorizontal: 20
    }}>

      <View
        style={{
          width: "90%",
          backgroundColor: "#1A1A1A",
          paddingVertical: 50,
          borderRadius: 24,
          alignItems: "center",
          borderColor: "#2A2A2A",
          borderWidth: 1
        }}
      >
        <Animated.View style={animatedStyle}>
          <Text
            style={{
              fontSize: 42,
              fontWeight: "700",
              color: "white",
              letterSpacing: 1
            }}
          >
            Design.
          </Text>
        </Animated.View>
      </View>

      <TouchableOpacity
        style={{
          marginTop: 40,
          backgroundColor: "#4A6CF7",
          paddingVertical: 14,
          paddingHorizontal: 50,
          borderRadius: 30,
        }}
        activeOpacity={0.8}
      >
        <Text style={{ color: "white", fontSize: 18, fontWeight: "600" }}>
          Get Started
        </Text>
      </TouchableOpacity>
    </View>
  );
}
Enter fullscreen mode Exit fullscreen mode

Do’s and Don’ts for Smooth Animations

✅ Do:

  • animate small components
  • use opacity and transform for best performance
  • test UI on low-end Android devices
  • keep transitions subtle and intentional

❌ Don’t:

  • animate large layout containers
  • animate width/height/layout properties
  • trigger too many animations at once
  • use heavy shadows & blurs

Final Thoughts

This guide covered the core pieces of Reanimated — shared values, timing, springs, and animated styles — and how they work together on the UI thread to create smooth, modern UI motion.

If you want to go deeper, the next step is exploring text animations, gesture-driven UI, sequencing multiple transitions, and advanced performance techniques.


Top comments (0)