Lottie is a powerful library for rendering Adobe After Effects animations natively in web and mobile applications. Here’s how to effectively integrate Lottie animations in your React projects.
1. Basic Lottie Integration
Installation
npm install lottie-react
# or
yarn add lottie-react
Basic Implementation
import Lottie from 'lottie-react';
import animationData from './animation.json';
function BasicAnimation() {
return <Lottie animationData={animationData} />;
}
2. Animation Control
Playback Control
import { useRef } from 'react';
import Lottie from 'lottie-react';
function ControlledAnimation() {
const lottieRef = useRef();
const playAnimation = () => {
lottieRef.current.play();
};
const pauseAnimation = () => {
lottieRef.current.pause();
};
const stopAnimation = () => {
lottieRef.current.stop();
};
return (
<div>
<Lottie
lottieRef={lottieRef}
animationData={animationData}
loop={false}
/>
<div className="controls">
<button onClick={playAnimation}>Play</button>
<button onClick={pauseAnimation}>Pause</button>
<button onClick={stopAnimation}>Stop</button>
</div>
</div>
);
}
Segment Playback
function SegmentAnimation() {
return (
<Lottie
animationData={animationData}
segments={[10, 50]} // Play frames 10-50
initialSegment={[0, 10]} // Initial segment (optional)
/>
);
}
3. Advanced Configuration
Event Handlers
function EventAnimation() {
const handleComplete = () => {
console.log('Animation completed!');
};
const handleLoopComplete = () => {
console.log('Loop completed!');
};
return (
<Lottie
animationData={animationData}
onComplete={handleComplete}
onLoopComplete={handleLoopComplete}
loop={true}
/>
);
}
Dynamic Loading
import { useState, useEffect } from 'react';
function DynamicAnimation({ animationPath }) {
const [animationData, setAnimationData] = useState(null);
useEffect(() => {
fetch(animationPath)
.then(response => response.json())
.then(data => setAnimationData(data));
}, [animationPath]);
if (!animationData) return <div>Loading animation...</div>;
return <Lottie animationData={animationData} />;
}
4. Performance Optimization
Lightweight Wrapper
import { useRef, useEffect } from 'react';
import lottie from 'lottie-web';
function LightweightLottie({ animationData }) {
const containerRef = useRef();
const animationRef = useRef();
useEffect(() => {
animationRef.current = lottie.loadAnimation({
container: containerRef.current,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: animationData,
});
return () => {
animationRef.current?.destroy();
};
}, [animationData]);
return <div ref={containerRef} style={{ width: 300, height: 300 }} />;
}
Intersection Observer (Lazy Loading)
import { useRef, useEffect, useState } from 'react';
function LazyLottie({ animationData }) {
const containerRef = useRef();
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.unobserve(entry.target);
}
},
{ threshold: 0.1 }
);
if (containerRef.current) {
observer.observe(containerRef.current);
}
return () => {
if (containerRef.current) {
observer.unobserve(containerRef.current);
}
};
}, []);
useEffect(() => {
if (!isVisible) return;
const anim = lottie.loadAnimation({
container: containerRef.current,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: animationData,
});
return () => anim.destroy();
}, [isVisible, animationData]);
return <div ref={containerRef} style={{ width: '100%', height: '100%' }} />;
}
5. Interactive Animations
Scroll-Triggered Animation
import { useRef, useEffect } from 'react';
import lottie from 'lottie-web';
function ScrollAnimation({ animationData }) {
const containerRef = useRef();
const animRef = useRef();
const scrollRef = useRef(0);
useEffect(() => {
animRef.current = lottie.loadAnimation({
container: containerRef.current,
renderer: 'svg',
animationData: animationData,
autoplay: false,
});
const handleScroll = () => {
const scrollPosition = window.scrollY;
const maxScroll = document.body.scrollHeight - window.innerHeight;
const scrollProgress = scrollPosition / maxScroll;
const totalFrames = animRef.current.totalFrames;
const targetFrame = Math.floor(scrollProgress * totalFrames);
animRef.current.goToAndStop(targetFrame, true);
};
window.addEventListener('scroll', handleScroll);
return () => {
animRef.current?.destroy();
window.removeEventListener('scroll', handleScroll);
};
}, [animationData]);
return <div ref={containerRef} style={{ height: '200vh' }} />;
}
Click-Triggered Animation
function ClickAnimation() {
const lottieRef = useRef();
const handleClick = () => {
lottieRef.current.playSegments([0, 30], true);
};
return (
<div onClick={handleClick} style={{ cursor: 'pointer' }}>
<Lottie
lottieRef={lottieRef}
animationData={animationData}
autoplay={false}
loop={false}
/>
</div>
);
}
6. Custom Styling and Theming
Dynamic Color Changes
import { useEffect, useRef } from 'react';
function ThemedAnimation({ primaryColor = '#ff0000' }) {
const animationRef = useRef();
useEffect(() => {
if (animationRef.current) {
animationRef.current.setColors({
keypath: 'Layer1.Shape1.Fill', // Path to color property in animation
color: primaryColor,
});
}
}, [primaryColor]);
return (
<Lottie
lottieRef={animationRef}
animationData={animationData}
/>
);
}
Responsive Sizing
function ResponsiveAnimation() {
return (
<div style={{ width: '100%', height: '100%', maxWidth: 600 }}>
<Lottie
animationData={animationData}
style={{ width: '100%', height: '100%' }}
/>
</div>
);
}
7. Advanced Patterns
Animation Chaining
function AnimationSequence() {
const firstAnimRef = useRef();
const secondAnimRef = useRef();
const playSequence = () => {
firstAnimRef.current.play();
firstAnimRef.current.addEventListener('complete', () => {
secondAnimRef.current.play();
});
};
return (
<div>
<Lottie
lottieRef={firstAnimRef}
animationData={firstAnimation}
loop={false}
autoplay={false}
/>
<Lottie
lottieRef={secondAnimRef}
animationData={secondAnimation}
loop={false}
autoplay={false}
/>
<button onClick={playSequence}>Play Sequence</button>
</div>
);
}
State-Driven Animations
function StatefulAnimation({ isActive }) {
const lottieRef = useRef();
useEffect(() => {
if (isActive) {
lottieRef.current.playSegments([0, 30], true);
} else {
lottieRef.current.playSegments([30, 60], true);
}
}, [isActive]);
return (
<Lottie
lottieRef={lottieRef}
animationData={animationData}
autoplay={false}
/>
);
}
Best Practices for Lottie in React
- Optimize JSON Files: Use tools like LottieFiles Optimizer to reduce file size
- Lazy Load: Only load animations when needed (especially for below-the-fold content)
- Reuse Instances: Cache animation data when using the same animation multiple times
- Fallbacks: Provide static image fallbacks for slow connections
- Performance Monitoring: Test animations on low-end devices
- Accessibility: Add ARIA labels and keyboard controls for interactive animations
- Cleanup: Always destroy animations on component unmount to prevent memory leaks
Troubleshooting Common Issues
Problem: Animation not playing
- Solution: Check if animationData is properly loaded
- Solution: Verify autoplay is enabled or manually trigger play()
Problem: Animation looks distorted
- Solution: Ensure container dimensions match animation’s aspect ratio
- Solution: Check for conflicting CSS transforms
Problem: Poor performance
- Solution: Reduce animation complexity in After Effects
- Solution: Use canvas renderer instead of SVG for complex animations
Problem: Colors not changing
- Solution: Verify color keypaths match the animation structure
- Solution: Check if animation has restricted color editing enabled
By implementing these Lottie animation techniques in your React applications, you can create rich, engaging user experiences with smooth, vector-based animations that work across all modern browsers and devices.