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:
Fast Transition
Normal Transition
Slow Transition
< 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 {
--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 ( 20 px );
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 {
--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 >
Apply different animations per platform:
< View className = "
ios:animate-bounce
android:animate-pulse
web:animate-spin
" >
< Text > Platform-specific animation </ Text >
</ View >
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.
Limit Concurrent Animations
Too many simultaneous animations can impact performance. Stagger animations or limit the number running at once.
Use Appropriate Durations
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 >
);
}
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.