About lottie
A Lottie animation implementation skill for developers working with web interfaces, including React. It covers file formats, performance, interaction, accessibility, loading, and cleanup.
Key features
- 01dotLottie format guidance
- 02Lazy loading and renderer selection
- 03Animation and image optimization
- 04Interactive playback patterns
- 05Reduced-motion and error fallbacks
- 06Loading and cleanup practices
Use cases
- 01Adding Lottie animations to React interfaces
- 02Reducing animation load and rendering costs
- 03Building scroll-linked or segment playback
- 04Supporting reduced-motion preferences
Install
Copy the command and run it in a terminal.
$
npx skills add https:/ / github.com/ mindrally/ skills --skill lottieAfter installing, type/lottiein Claude Code to confirm it loaded.
Details
From the project READMELottie Animation Guidelines
You are an expert in Lottie animations, web performance, and JavaScript. Follow these guidelines when implementing Lottie animations.
Core Principles
Use dotLottie Format
- Prefer
.lottie(dotLottie) format over.json- up to 90% smaller file size - dotLottie bundles all assets (images, fonts) into a single compressed file
- Use the free dotLottie converter at lottiefiles.com
Installation
# For React
npm install @lottiefiles/dotlottie-react
# For vanilla JS
npm install @lottiefiles/dotlottie-web
React Implementation
Basic Usage
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
function Animation() {
return (
<DotLottieReact
src="/animations/loading.lottie"
loop
autoplay
/>
);
}
Control Animation Playback
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
import { useState } from "react";
function ControlledAnimation() {
const [dotLottie, setDotLottie] = useState(null);
const dotLottieRefCallback = (dotLottie) => {
setDotLottie(dotLottie);
};
return (
<>
<DotLottieReact
src="/animation.lottie"
dotLottieRefCallback={dotLottieRefCallback}
/>
<button onClick={() => dotLottie?.play()}>Play</button>
<button onClick={() => dotLottie?.pause()}>Pause</button>
<button onClick={() => dotLottie?.stop()}>Stop</button>
</>
);
}
Performance Optimization
Lazy Loading
import { useEffect, useRef, useState } from "react";
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
function LazyLottie({ src }) {
const [isVisible, setIsVisible] = useState(false);
const containerRef = useRef(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.disconnect();
}
},
{ rootMargin: "100px" }
);
if (containerRef.current) {
observer.observe(containerRef.current);
}
return () => observer.disconnect();
}, []);
return (
<div ref={containerRef}>
{isVisible && <DotLottieReact src={src} autoplay loop />}
</div>
);
}
Choose the Right Renderer
// SVG renderer - best quality, good for simple animations
<DotLottieReact src="/animation.lottie" renderer="svg" />
// Canvas renderer - better performance for complex animations
<DotLottieReact src="/animation.lottie" renderer="canvas" />
// Use canvas for:
// - Complex animations with many elements
// - Lower-powered devices
// - Animations with filters/effects
Reduce DOM Elements
- Reuse identical graphic elements in After Effects
- Simplify paths and reduce keyframes
- Avoid unnecessary layers
- Target under 1000 DOM elements per animation
Animation Design Best Practices
Avoid Performance-Heavy Features
AVOID:
- Masks (use alpha matte sparingly)
- Complex blur effects
- 3D layers
- Expressions
- Uncompressed images
- Large image assets
PREFER:
- Simple shapes (fills, strokes)
- Transform animations (position, scale, rotation)
- Opacity changes
- Path animations
Optimize Images in Animations
- Compress images to match display size
- If max display is 400x400, don't use 1000x1000 images
- Use vector graphics when possible
- Consider converting images to shapes
Interactivity
Cursor/Mouse Interaction
<DotLottieReact
src="/hover-animation.lottie"
playMode="hover"
/>
Scroll-Linked Animation
import { useScroll, useTransform } from "motion/react";
function ScrollLottie() {
const { scrollYProgress } = useScroll();
const [dotLottie, setDotLottie] = useState(null);
useEffect(() => {
if (!dotLottie) return;
const unsubscribe = scrollYProgress.on("change", (progress) => {
dotLottie.setFrame(progress * dotLottie.totalFrames);
});
return unsubscribe;
}, [dotLottie, scrollYProgress]);
return (
<DotLottieReact
src="/scroll-animation.lottie"
dotLottieRefCallback={setDotLottie}
autoplay={false}
/>
);
}
Segment Playback
function SegmentAnimation() {
const [dotLottie, setDotLottie] = useState(null);
const playSegment = (start, end) => {
dotLottie?.setSegment(start, end);
dotLottie?.play();
};
return (
<>
<DotLottieReact
src="/multi-state.lottie"
dotLottieRefCallback={setDotLottie}
autoplay={false}
/>
<button onClick={() => playSegment(0, 30)}>State 1</button>
<button onClick={() => playSegment(30, 60)}>State 2</button>
</>
);
}
Accessibility
Respect Reduced Motion
function AccessibleAnimation() {
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
if (prefersReducedMotion) {
return <img src="/static-fallback.svg" alt="Animation description" />;
}
return (
<DotLottieReact
src="/animation.lottie"
autoplay
loop
aria-label="Decorative loading animation"
/>
);
}
Provide Fallbacks
function AnimationWithFallback() {
const [hasError, setHasError] = useState(false);
if (hasError) {
return <img src="/fallback.gif" alt="Animation" />;
}
return (
<DotLottieReact
src="/animation.lottie"
autoplay
onError={() => setHasError(true)}
/>
);
}
Loading Strategy
Use Preloader for Large Animations
function AnimationWithPreloader() {
const [isLoaded, setIsLoaded] = useState(false);
return (
<div className="animation-container">
{!isLoaded && (
<img src="/first-frame.webp" alt="" className="preloader" />
)}
<DotLottieReact
src="/large-animation.lottie"
onLoad={() => setIsLoaded(true)}
style={{ opacity: isLoaded ? 1 : 0 }}
autoplay
/>
</div>
);
}
File Size Guidelines
| Animation Complexity | Target Size | Max DOM Elements |
|---|---|---|
| Simple icons | < 10KB | < 100 |
| UI animations | < 50KB | < 500 |
| Complex scenes | < 150KB | < 1500 |
| Hero animations | < 300KB | < 2500 |
Cleanup
Proper Cleanup in React
useEffect(() => {
return () => {
dotLottie?.destroy();
};
}, [dotLottie]);
Best Practices Summary
- Use dotLottie format for smaller file sizes
- Lazy load animations not in viewport
- Use canvas renderer for complex animations
- Avoid masks, blurs, and expressions
- Compress and optimize image assets
- Respect reduced motion preferences
- Provide static fallbacks for errors
- Clean up animations on unmount
- Keep DOM element count low
- Use preloaders for large animations
Comments
Markdown supported