Skip to main content

Overview

NativeWind supports Tailwind’s animation and transition utilities, automatically translating them to performant React Native animations. The framework intelligently uses the best animation API for your platform - CSS animations on web and native animation APIs on iOS and Android.

Basic Animations

Built-in Animations

NativeWind includes Tailwind’s default animations:
import { View, Text } from "react-native";

export default function AnimatedComponents() {
  return (
    <View className="flex-1 items-center justify-center p-4">
      {/* Spin animation */}
      <View className="w-16 h-16 bg-blue-500 animate-spin" />
      
      {/* Ping animation */}
      <View className="w-16 h-16 bg-green-500 animate-ping mt-4" />
      
      {/* Pulse animation */}
      <View className="w-16 h-16 bg-purple-500 animate-pulse mt-4" />
      
      {/* Bounce animation */}
      <View className="w-16 h-16 bg-red-500 animate-bounce mt-4" />
    </View>
  );
}
Animations on React Native are powered by the platform’s native animation APIs for optimal performance. On web, they use CSS animations.

Transitions

Transition Properties

Control which properties animate with transition utilities:
import { View, Text, Pressable } from "react-native";
import { useState } from "react";

export default function TransitionDemo() {
  const [isPressed, setIsPressed] = useState(false);
  
  return (
    <Pressable
      onPressIn={() => setIsPressed(true)}
      onPressOut={() => setIsPressed(false)}
    >
      <View
        className={`
          w-32 h-32 bg-blue-500 rounded-lg
          transition-all duration-300
          ${isPressed ? 'scale-95 bg-blue-700' : 'scale-100'}
        `}
      >
        <Text className="text-white text-center mt-12">
          Press Me
        </Text>
      </View>
    </Pressable>
  );
}

Transition Duration

Control animation timing with duration utilities:
<View className="transition-all duration-150 ...">
  {/* 150ms transition */}
</View>
Available durations:
  • duration-75 - 75ms
  • duration-100 - 100ms
  • duration-150 - 150ms
  • duration-200 - 200ms
  • duration-300 - 300ms
  • duration-500 - 500ms
  • duration-700 - 700ms
  • duration-1000 - 1000ms

Transition Timing Functions

Control the easing curve of transitions:
{/* Linear easing */}
<View className="transition-all duration-300 ease-linear" />

{/* Ease in */}
<View className="transition-all duration-300 ease-in" />

{/* Ease out */}
<View className="transition-all duration-300 ease-out" />

{/* Ease in-out */}
<View className="transition-all duration-300 ease-in-out" />

Custom Animations

Define custom animations in your theme:
theme.css
@theme {
  --animate-fade-in: fadeIn 0.5s ease-in;
  --animate-slide-up: slideUp 0.3s ease-out;
  --animate-scale-in: scaleIn 0.2s ease-out;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes slideUp {
  from {
    transform: translateY(20px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

@keyframes scaleIn {
  from {
    transform: scale(0.9);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}
Usage:
<View className="animate-fade-in">
  <Text>Fades in on mount</Text>
</View>

<View className="animate-slide-up">
  <Text>Slides up on mount</Text>
</View>

<View className="animate-scale-in">
  <Text>Scales in on mount</Text>
</View>

Interactive Animations

Press Animations

Create interactive press feedback:
import { Pressable, Text, View } from "react-native";

function AnimatedButton() {
  return (
    <Pressable>
      {({ pressed }) => (
        <View
          className={`
            bg-blue-500 px-6 py-3 rounded-lg
            transition-all duration-150
            ${pressed ? 'scale-95 bg-blue-600' : 'scale-100'}
          `}
        >
          <Text className="text-white font-semibold text-center">
            Press Me
          </Text>
        </View>
      )}
    </Pressable>
  );
}

Hover Animations (Web)

On web platforms, use hover states:
<View className="
  bg-white p-4 rounded-lg
  transition-all duration-300
  web:hover:shadow-lg web:hover:scale-105
">
  <Text>Hover over me (web only)</Text>
</View>

Loading States

Skeleton Loading

Create skeleton screens with pulse animations:
export default function SkeletonLoader() {
  return (
    <View className="p-4 space-y-4">
      {/* Avatar skeleton */}
      <View className="w-12 h-12 bg-gray-300 rounded-full animate-pulse" />
      
      {/* Text skeletons */}
      <View className="space-y-2">
        <View className="h-4 bg-gray-300 rounded animate-pulse" />
        <View className="h-4 bg-gray-300 rounded w-5/6 animate-pulse" />
        <View className="h-4 bg-gray-300 rounded w-4/6 animate-pulse" />
      </View>
      
      {/* Card skeleton */}
      <View className="h-32 bg-gray-300 rounded-lg animate-pulse" />
    </View>
  );
}

Spinner

Create a loading spinner:
import { View, Text } from "react-native";

export default function LoadingSpinner() {
  return (
    <View className="flex-1 items-center justify-center">
      <View className="w-16 h-16 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" />
      <Text className="mt-4 text-gray-600">Loading...</Text>
    </View>
  );
}

Staggered Animations

Create staggered animation effects:
import { View, Text } from "react-native";

const items = [
  { id: 1, delay: 'delay-0' },
  { id: 2, delay: 'delay-75' },
  { id: 3, delay: 'delay-150' },
  { id: 4, delay: 'delay-300' },
];

export default function StaggeredList() {
  return (
    <View className="p-4">
      {items.map((item) => (
        <View
          key={item.id}
          className={`
            bg-white p-4 mb-2 rounded-lg
            animate-fade-in ${item.delay}
          `}
        >
          <Text>Item {item.id}</Text>
        </View>
      ))}
    </View>
  );
}

Animation Delays

Control when animations start:
theme.css
@theme {
  --animate-delay-0: 0ms;
  --animate-delay-75: 75ms;
  --animate-delay-100: 100ms;
  --animate-delay-150: 150ms;
  --animate-delay-200: 200ms;
  --animate-delay-300: 300ms;
  --animate-delay-500: 500ms;
  --animate-delay-700: 700ms;
  --animate-delay-1000: 1000ms;
}
Usage:
<View className="animate-fade-in delay-300">
  <Text>Appears after 300ms</Text>
</View>

Platform-Specific Animations

Apply different animations per platform:
<View className="
  ios:animate-bounce
  android:animate-pulse
  web:animate-spin
">
  <Text>Platform-specific animation</Text>
</View>

Performance Best Practices

NativeWind automatically uses the native animation driver when possible for transforms and opacity, ensuring 60fps animations.
Animating layout properties (width, height, position) can be expensive. Prefer transforms (scale, translate) and opacity.
Too many simultaneous animations can impact performance. Stagger animations or limit the number running at once.
Short, snappy animations (150-300ms) feel more responsive than long animations (500ms+).

Common Animation Patterns

Card Entrance

function AnimatedCard() {
  return (
    <View className="
      bg-white p-6 rounded-lg shadow-lg
      animate-fade-in
    ">
      <Text className="text-xl font-bold">Welcome!</Text>
      <Text className="text-gray-600 mt-2">This card animates in smoothly</Text>
    </View>
  );
}

Button Feedback

function FeedbackButton() {
  return (
    <Pressable>
      {({ pressed }) => (
        <View className={`
          bg-blue-500 px-6 py-3 rounded-full
          transition-all duration-100
          ${pressed ? 'scale-90 opacity-80' : 'scale-100 opacity-100'}
        `}>
          <Text className="text-white font-semibold">Submit</Text>
        </View>
      )}
    </Pressable>
  );
}

Toggle Animation

import { useState } from "react";
import { Pressable, View, Text } from "react-native";

function ToggleSwitch() {
  const [enabled, setEnabled] = useState(false);
  
  return (
    <Pressable onPress={() => setEnabled(!enabled)}>
      <View className={`
        w-14 h-8 rounded-full p-1
        transition-colors duration-200
        ${enabled ? 'bg-blue-500' : 'bg-gray-300'}
      `}>
        <View className={`
          w-6 h-6 bg-white rounded-full
          transition-transform duration-200
          ${enabled ? 'translate-x-6' : 'translate-x-0'}
        `} />
      </View>
    </Pressable>
  );
}

Progress Indicator

import { View, Text } from "react-native";

function ProgressBar({ progress }: { progress: number }) {
  return (
    <View className="w-full">
      <View className="h-2 bg-gray-200 rounded-full overflow-hidden">
        <View
          className="h-full bg-blue-500 transition-all duration-300"
          style={{ width: `${progress}%` }}
        />
      </View>
      <Text className="text-xs text-gray-600 mt-1">
        {progress}% complete
      </Text>
    </View>
  );
}

Accessibility

Respect user preferences for reduced motion:
import { AccessibilityInfo } from "react-native";
import { useEffect, useState } from "react";

function AccessibleAnimation() {
  const [reduceMotion, setReduceMotion] = useState(false);
  
  useEffect(() => {
    AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion);
  }, []);
  
  return (
    <View className={`
      bg-blue-500 p-4 rounded-lg
      ${reduceMotion ? '' : 'animate-bounce'}
    `}>
      <Text className="text-white">
        Respects reduced motion preference
      </Text>
    </View>
  );
}
Always test animations on actual devices, not just simulators. Some animations may perform differently on real hardware.

Advanced: Combining with React Native Reanimated

For complex animations, combine NativeWind with React Native Reanimated:
import Animated, { 
  useAnimatedStyle, 
  withSpring 
} from 'react-native-reanimated';
import { Pressable } from 'react-native';

function ReanimatedButton() {
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: withSpring(pressed ? 0.9 : 1) }],
  }));
  
  return (
    <Pressable>
      <Animated.View
        style={animatedStyle}
        className="bg-blue-500 px-6 py-3 rounded-lg"
      >
        <Text className="text-white font-semibold">Advanced Animation</Text>
      </Animated.View>
    </Pressable>
  );
}
NativeWind’s built-in animations are sufficient for most use cases. Use Reanimated for gesture-driven or physics-based animations.