Easy to use declarative transitions and animations for React Native
—
Method-based animation control for complex programmatic animations and transitions. Execute animations directly via component methods for fine-grained control over timing and sequencing.
Execute any animation imperatively with optional timing parameters.
/**
* Execute an animation imperatively
* @param animation - Animation name or custom definition
* @param duration - Optional duration override in milliseconds
* @param iterationDelay - Optional delay between iterations in milliseconds
* @returns Promise that resolves when animation completes or is cancelled
*/
animate(animation: Animation | CustomAnimation, duration?: number, iterationDelay?: number): Promise<{ finished: boolean }>;Usage Examples:
class AnimatedComponent extends Component {
handleViewRef = ref => this.view = ref;
startAnimation = async () => {
try {
// Execute animation and wait for completion
const endState = await this.view.animate('bounceIn', 800);
if (endState.finished) {
console.log('Animation completed successfully');
this.startNextAnimation();
} else {
console.log('Animation was cancelled');
}
} catch (error) {
console.log('Animation error:', error);
}
};
customAnimation = () => {
// Use custom animation object
const customBounce = {
0: { scale: 1, translateY: 0 },
0.5: { scale: 1.2, translateY: -50 },
1: { scale: 1, translateY: 0 }
};
this.view.animate(customBounce, 1000);
};
render() {
return (
<Animatable.View ref={this.handleViewRef}>
<TouchableOpacity onPress={this.startAnimation}>
<Text>Tap to animate</Text>
</TouchableOpacity>
</Animatable.View>
);
}
}All built-in animations are available as direct methods on animatable components.
interface AnimationMethods {
// Attention seekers
bounce(duration?: number): Promise<{ finished: boolean }>;
flash(duration?: number): Promise<{ finished: boolean }>;
jello(duration?: number): Promise<{ finished: boolean }>;
pulse(duration?: number): Promise<{ finished: boolean }>;
rotate(duration?: number): Promise<{ finished: boolean }>;
rubberBand(duration?: number): Promise<{ finished: boolean }>;
shake(duration?: number): Promise<{ finished: boolean }>;
swing(duration?: number): Promise<{ finished: boolean }>;
tada(duration?: number): Promise<{ finished: boolean }>;
wobble(duration?: number): Promise<{ finished: boolean }>;
// Bouncing entrances
bounceIn(duration?: number): Promise<{ finished: boolean }>;
bounceInDown(duration?: number): Promise<{ finished: boolean }>;
bounceInUp(duration?: number): Promise<{ finished: boolean }>;
bounceInLeft(duration?: number): Promise<{ finished: boolean }>;
bounceInRight(duration?: number): Promise<{ finished: boolean }>;
// ... all other built-in animations
}Usage Examples:
class InteractiveComponent extends Component {
handleViewRef = ref => this.view = ref;
// Different methods for different interactions
onPress = () => this.view.pulse(200);
onLongPress = () => this.view.shake(400);
onSuccess = () => this.view.bounce(600);
onError = () => this.view.flash(300);
sequentialAnimations = async () => {
// Chain animations sequentially
await this.view.slideInLeft(500);
await this.view.pulse(300);
await this.view.slideOutRight(500);
};
render() {
return (
<TouchableOpacity
onPress={this.onPress}
onLongPress={this.onLongPress}
>
<Animatable.View ref={this.handleViewRef}>
<Text>Interactive Element</Text>
</Animatable.View>
</TouchableOpacity>
);
}
}Stop any currently running animation immediately.
/**
* Stop any currently running animation
*/
stopAnimation(): void;Usage Examples:
class ControlledAnimation extends Component {
handleViewRef = ref => this.view = ref;
startInfiniteAnimation = () => {
// Start infinite animation
this.view.animate('pulse', 1000);
// Set up auto-stop after 5 seconds
this.stopTimer = setTimeout(() => {
this.view.stopAnimation();
}, 5000);
};
stopAnimation = () => {
// Stop animation immediately
this.view.stopAnimation();
if (this.stopTimer) {
clearTimeout(this.stopTimer);
}
};
componentWillUnmount() {
// Always stop animations when component unmounts
this.view?.stopAnimation();
if (this.stopTimer) {
clearTimeout(this.stopTimer);
}
};
render() {
return (
<View>
<Animatable.View ref={this.handleViewRef}>
<Text>Controlled Animation</Text>
</Animatable.View>
<Button title="Start" onPress={this.startInfiniteAnimation} />
<Button title="Stop" onPress={this.stopAnimation} />
</View>
);
}
}Transition between specific style values with full control over from and to states.
/**
* Transition between specific style values
* @param fromValues - Starting style values
* @param toValues - Ending style values
* @param duration - Optional transition duration in milliseconds
* @param easing - Optional easing function
*/
transition(fromValues: object, toValues: object, duration?: number, easing?: Easing): void;Usage Examples:
class TransitionComponent extends Component {
handleViewRef = ref => this.view = ref;
colorTransition = () => {
this.view.transition(
{ backgroundColor: 'red', scale: 1 }, // From
{ backgroundColor: 'blue', scale: 1.2 }, // To
800, // Duration
'ease-in-out' // Easing
);
};
layoutTransition = () => {
this.view.transition(
{
width: 100,
height: 100,
borderRadius: 0
},
{
width: 200,
height: 50,
borderRadius: 25
},
1000,
'ease-out-back'
);
};
multiPropertyTransition = () => {
this.view.transition(
{
opacity: 1,
translateX: 0,
translateY: 0,
rotate: '0deg',
scale: 1
},
{
opacity: 0.7,
translateX: 100,
translateY: 50,
rotate: '45deg',
scale: 1.5
},
1200
);
};
render() {
return (
<View>
<Animatable.View ref={this.handleViewRef} style={styles.box}>
<Text>Transition Box</Text>
</Animatable.View>
<Button title="Color Transition" onPress={this.colorTransition} />
<Button title="Layout Transition" onPress={this.layoutTransition} />
<Button title="Multi-Property" onPress={this.multiPropertyTransition} />
</View>
);
}
}Transition to specific style values from the current state automatically.
/**
* Transition to specific style values from current state
* @param toValues - Target style values
* @param duration - Optional transition duration in milliseconds
* @param easing - Optional easing function
*/
transitionTo(toValues: object, duration?: number, easing?: Easing): void;Usage Examples:
class SmartTransition extends Component {
handleViewRef = ref => this.view = ref;
state = {
expanded: false,
highlighted: false
};
toggleExpanded = () => {
const expanded = !this.state.expanded;
this.setState({ expanded });
// Transition to new size
this.view.transitionTo({
width: expanded ? 300 : 150,
height: expanded ? 200 : 100,
scale: expanded ? 1.1 : 1
}, 400, 'ease-out');
};
toggleHighlight = () => {
const highlighted = !this.state.highlighted;
this.setState({ highlighted });
// Transition to new appearance
this.view.transitionTo({
backgroundColor: highlighted ? 'yellow' : 'white',
opacity: highlighted ? 1 : 0.8,
borderWidth: highlighted ? 3 : 1
}, 200);
};
animateToRandomPosition = () => {
// Transition to random position
this.view.transitionTo({
translateX: Math.random() * 200 - 100,
translateY: Math.random() * 200 - 100,
rotate: `${Math.random() * 360}deg`
}, 800, 'ease-in-out');
};
resetPosition = () => {
// Return to original position
this.view.transitionTo({
translateX: 0,
translateY: 0,
rotate: '0deg',
scale: 1,
opacity: 1
}, 600, 'ease-out-back');
};
render() {
return (
<View>
<Animatable.View
ref={this.handleViewRef}
style={[
styles.box,
{
width: 150,
height: 100,
backgroundColor: 'white',
borderWidth: 1,
borderColor: 'gray'
}
]}
>
<Text>Smart Transition</Text>
</Animatable.View>
<Button title="Toggle Size" onPress={this.toggleExpanded} />
<Button title="Toggle Highlight" onPress={this.toggleHighlight} />
<Button title="Random Position" onPress={this.animateToRandomPosition} />
<Button title="Reset" onPress={this.resetPosition} />
</View>
);
}
}Create complex animation sequences with promises:
class SequenceAnimation extends Component {
handleViewRef = ref => this.view = ref;
complexSequence = async () => {
try {
// Step 1: Slide in
await this.view.slideInLeft(500);
// Step 2: Pulse 3 times
for (let i = 0; i < 3; i++) {
await this.view.pulse(300);
if (i < 2) await this.wait(200); // Pause between pulses
}
// Step 3: Scale up and change color
this.view.transitionTo({
scale: 1.5,
backgroundColor: 'gold'
}, 400);
await this.wait(400);
// Step 4: Final bounce and slide out
await this.view.bounce(600);
await this.view.slideOutRight(500);
} catch (error) {
console.log('Sequence interrupted:', error);
}
};
wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
render() {
return (
<Animatable.View ref={this.handleViewRef}>
<TouchableOpacity onPress={this.complexSequence}>
<Text>Start Complex Sequence</Text>
</TouchableOpacity>
</Animatable.View>
);
}
}class ConditionalAnimation extends Component {
handleViewRef = ref => this.view = ref;
smartAnimation = async () => {
const startTime = Date.now();
// Start with attention seeker
const result = await this.view.shake(400);
if (result.finished) {
// If user hasn't interacted, continue with more animations
if (Date.now() - startTime > 2000) {
await this.view.bounce(600);
await this.view.pulse(300);
}
} else {
// Animation was cancelled, do cleanup
this.resetState();
}
};
interruptibleSequence = async () => {
this.animationCancelled = false;
const animations = ['fadeIn', 'pulse', 'shake', 'bounce'];
for (const animation of animations) {
if (this.animationCancelled) break;
const result = await this.view.animate(animation, 600);
if (!result.finished) break;
await this.wait(300);
}
};
cancelSequence = () => {
this.animationCancelled = true;
this.view.stopAnimation();
};
}class OptimizedAnimations extends Component {
handleViewRef = ref => this.view = ref;
// Use native driver for transform animations
nativeTransforms = () => {
// These work with native driver
this.view.transitionTo({
translateX: 100,
translateY: 50,
scale: 1.2,
rotate: '45deg',
opacity: 0.8
}, 400);
};
// Use JS driver for layout/color animations
jsAnimations = () => {
// These require JS driver
this.view.transitionTo({
backgroundColor: 'red',
width: 200,
height: 150,
borderRadius: 20
}, 400);
};
// Batch multiple transform animations
batchedTransforms = () => {
// More efficient than multiple separate calls
this.view.transitionTo({
translateX: 50,
translateY: -30,
scale: 1.1,
rotate: '15deg'
}, 300);
};
}class RobustAnimations extends Component {
animationPromises = [];
handleViewRef = ref => this.view = ref;
safeAnimation = async () => {
try {
const animationPromise = this.view.bounce(800);
this.animationPromises.push(animationPromise);
const result = await animationPromise;
// Remove completed promise
this.animationPromises = this.animationPromises.filter(p => p !== animationPromise);
if (result.finished) {
this.onAnimationComplete();
} else {
this.onAnimationCancelled();
}
} catch (error) {
console.error('Animation error:', error);
this.onAnimationError(error);
}
};
componentWillUnmount() {
// Stop all animations and clear promises
this.view?.stopAnimation();
this.animationPromises = [];
};
onAnimationComplete = () => {
console.log('Animation completed successfully');
};
onAnimationCancelled = () => {
console.log('Animation was cancelled');
};
onAnimationError = (error) => {
console.error('Animation failed:', error);
};
}Install with Tessl CLI
npx tessl i tessl/npm-react-native-animatable