
Lottie Animations
- 1.7k installs
- 629 repo stars
- Updated November 20, 2025
- freshtechbro/claudedesignskills
lottie-animations is an agent skill that after effects animation rendering for web and react applications. use this skill when implementing lottie animations, json vector animations, interactive animated icons, micro-int
About
lottie-animations is an agent skill from freshtechbro/claudedesignskills that after effects animation rendering for web and react applications. use this skill when implementing lottie animations, json vector animations, interactive animated icons, micro-interactions, or loading. # Lottie Animations ## Overview Lottie is a library for rendering After Effects animations in real-time on web, iOS, Android, and React Native. Created by Airbnb, it allows designers to ship animations as easily as shipping static assets. Animations are exported from After Effects as JSON files using the Bodymovin plugin, then rendered natively w Developers invoke lottie-animations during build/integrations work for generative media tasks. The skill documents triggers, prerequisites, and step-by-step workflows grounded in SKILL.md. Compatible with Claude Code, Cursor, and Codex agent runtimes that load marketplace skills. Review the Security Audits panel on this listing before installing in production environments.
- Designer-created animations that need pixel-perfect fidelity
- Complex animated icons and micro-interactions
- Loading animations and progress indicators
- Onboarding sequences and tutorial animations
- Marketing animations and promotional content
Lottie Animations by the numbers
- 1,733 all-time installs (skills.sh)
- +110 installs in the week ending Jul 29, 2026 (Skillselion tracking)
- Ranked #174 of 1,337 Generative Media skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
lottie-animations capabilities & compatibility
- Capabilities
- designer created animations that need pixel perf · complex animated icons and micro interactions · loading animations and progress indicators · onboarding sequences and tutorial animations · marketing animations and promotional content
- Use cases
- orchestration
What lottie-animations says it does
- Designer-created animations that need pixel-perfect fidelity
- Complex animated icons and micro-interactions
- Loading animations and progress indicators
npx skills add https://github.com/freshtechbro/claudedesignskills --skill lottie-animationsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.7k |
|---|---|
| repo stars | ★ 629 |
| Security audit | 2 / 3 scanners passed |
| Last updated | November 20, 2025 |
| Repository | freshtechbro/claudedesignskills ↗ |
What it does
After Effects animation rendering for web and React applications. Use this skill when implementing Lottie animations, JSON vector animations, interactive animated icons, micro-interactions, or loading
Who is it for?
Developers working on generative media during build tasks.
Skip if: Tasks outside Generative Media scope described in SKILL.md.
When should I use this skill?
After Effects animation rendering for web and React applications. Use this skill when implementing Lottie animations, JSON vector animations, interactive animated icons, micro-interactions, or loading
What you get
Completed generative media workflow aligned with SKILL.md steps.
- React Vite project scaffold
- DotLottie example components
By the numbers
- Includes 2 example Lottie components: BasicAnimation and InteractiveAnimation
Files
Lottie Animations
Overview
Lottie is a library for rendering After Effects animations in real-time on web, iOS, Android, and React Native. Created by Airbnb, it allows designers to ship animations as easily as shipping static assets. Animations are exported from After Effects as JSON files using the Bodymovin plugin, then rendered natively with minimal performance overhead.
When to use Lottie:
- Designer-created animations that need pixel-perfect fidelity
- Complex animated icons and micro-interactions
- Loading animations and progress indicators
- Onboarding sequences and tutorial animations
- Marketing animations and promotional content
- Alternative to GIF/video with smaller file sizes and scalability
Key advantages:
- Vector-based (scalable without quality loss)
- Significantly smaller file sizes than GIF or video
- Editable at runtime (colors, speed, segments)
- Full designer control via After Effects
- Cross-platform rendering consistency
- Interactive controls (play, pause, seek, loop)
Core Concepts
Lottie Format Types
1. JSON Lottie (.json)
- Original Lottie format
- Exported from After Effects via Bodymovin plugin
- Human-readable JSON structure
- Larger file sizes (not compressed)
- Widely supported across all platforms
2. dotLottie (.lottie)
- Modern compressed format
- ZIP archive containing JSON + assets
- Supports multiple animations and themes in one file
- Smaller file sizes (up to 90% reduction)
- Recommended for production use
Library Options
lottie-web (original library):
import lottie from 'lottie-web';
lottie.loadAnimation({
container: document.getElementById('lottie-container'),
renderer: 'svg', // or 'canvas', 'html'
loop: true,
autoplay: true,
path: 'animation.json' // or animationData: jsonData
});@lottiefiles/dotlottie-web (modern, recommended):
import { DotLottie } from '@lottiefiles/dotlottie-web';
new DotLottie({
canvas: document.getElementById('canvas'),
src: 'animation.lottie',
autoplay: true,
loop: true
});@lottiefiles/dotlottie-react (React integration):
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
<DotLottieReact
src="animation.lottie"
loop
autoplay
style={{ height: 300 }}
/>lottie-react (alternative React wrapper):
import Lottie from 'lottie-react';
import animationData from './animation.json';
<Lottie animationData={animationData} loop={true} />Animation Data Sources
1. LottieFiles (lottie.host)
- 100,000+ free animations
- Direct URL embedding
- CDN hosting
2. Local JSON/dotLottie files
- Bundled with application
- Better performance (no network request)
- Version control friendly
3. After Effects export
- Custom designer animations
- Bodymovin plugin required
- Export settings critical for file size
Common Patterns
1. Basic HTML Integration with dotLottie-web
<!DOCTYPE html>
<html>
<head>
<style>
#canvas {
width: 400px;
height: 400px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="module">
import { DotLottie } from 'https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web/+esm';
new DotLottie({
canvas: document.getElementById('canvas'),
src: 'https://lottie.host/4db68bbd-31f6-4cd8-84eb-189de081159a/IGmMCqhzpt.lottie',
autoplay: true,
loop: true
});
</script>
</body>
</html>2. React Component with Controls
import React from 'react';
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
const AnimatedButton = () => {
const [dotLottie, setDotLottie] = React.useState(null);
const handlePlay = () => dotLottie?.play();
const handlePause = () => dotLottie?.pause();
const handleStop = () => dotLottie?.stop();
const handleSeek = (frame) => dotLottie?.setFrame(frame);
return (
<div>
<DotLottieReact
src="button-animation.lottie"
loop
autoplay={false}
dotLottieRefCallback={setDotLottie}
style={{ height: 200 }}
/>
<div>
<button onClick={handlePlay}>Play</button>
<button onClick={handlePause}>Pause</button>
<button onClick={handleStop}>Stop</button>
<button onClick={() => handleSeek(30)}>Seek to frame 30</button>
</div>
</div>
);
};3. Event Listeners and Lifecycle Hooks
import React, { useEffect } from 'react';
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
const EventDrivenAnimation = () => {
const [dotLottie, setDotLottie] = React.useState(null);
useEffect(() => {
if (!dotLottie) return;
const onLoad = () => console.log('Animation loaded');
const onPlay = () => console.log('Animation started');
const onPause = () => console.log('Animation paused');
const onComplete = () => console.log('Animation completed');
const onFrame = ({ currentFrame }) => console.log('Frame:', currentFrame);
dotLottie.addEventListener('load', onLoad);
dotLottie.addEventListener('play', onPlay);
dotLottie.addEventListener('pause', onPause);
dotLottie.addEventListener('complete', onComplete);
dotLottie.addEventListener('frame', onFrame);
return () => {
dotLottie.removeEventListener('load', onLoad);
dotLottie.removeEventListener('play', onPlay);
dotLottie.removeEventListener('pause', onPause);
dotLottie.removeEventListener('complete', onComplete);
dotLottie.removeEventListener('frame', onFrame);
};
}, [dotLottie]);
return (
<DotLottieReact
src="animation.lottie"
loop
autoplay
dotLottieRefCallback={setDotLottie}
/>
);
};4. Scroll-Driven Animation with lottie-react
import Lottie from 'lottie-react';
import robotAnimation from './robot.json';
const ScrollAnimation = () => {
const interactivity = {
mode: 'scroll',
actions: [
{
visibility: [0, 0.2],
type: 'stop',
frames: [0]
},
{
visibility: [0.2, 0.45],
type: 'seek',
frames: [0, 45]
},
{
visibility: [0.45, 1.0],
type: 'loop',
frames: [45, 60]
}
]
};
return (
<Lottie
animationData={robotAnimation}
style={{ height: 300 }}
interactivity={interactivity}
/>
);
};5. Hover-Triggered Segment Playback
import { useLottie, useLottieInteractivity } from 'lottie-react';
import likeButton from './like-button.json';
const HoverAnimation = () => {
const lottieObj = useLottie({
animationData: likeButton
});
const Animation = useLottieInteractivity({
lottieObj,
mode: 'cursor',
actions: [
{
position: { x: [0, 1], y: [0, 1] },
type: 'loop',
frames: [45, 60]
},
{
position: { x: -1, y: -1 },
type: 'stop',
frames: [45]
}
]
});
return <div style={{ height: 300, border: '2px solid black' }}>{Animation}</div>;
};6. Multi-Animation and Theme Support
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
import React, { useState, useEffect } from 'react';
const ThemedAnimation = () => {
const [dotLottie, setDotLottie] = useState(null);
const [animations, setAnimations] = useState([]);
const [themes, setThemes] = useState([]);
const [currentAnimationId, setCurrentAnimationId] = useState('');
const [currentThemeId, setCurrentThemeId] = useState('');
useEffect(() => {
if (!dotLottie) return;
const onLoad = () => {
setAnimations(dotLottie.manifest.animations || []);
setThemes(dotLottie.manifest.themes || []);
setCurrentAnimationId(dotLottie.activeAnimationId);
setCurrentThemeId(dotLottie.activeThemeId);
};
dotLottie.addEventListener('load', onLoad);
return () => dotLottie.removeEventListener('load', onLoad);
}, [dotLottie]);
return (
<div>
<DotLottieReact
src="multi-animation.lottie"
dotLottieRefCallback={setDotLottie}
animationId={currentAnimationId}
themeId={currentThemeId}
/>
{themes.length > 0 && (
<select value={currentThemeId} onChange={(e) => setCurrentThemeId(e.target.value)}>
{themes.map((theme) => (
<option key={theme.id} value={theme.id}>{theme.id}</option>
))}
</select>
)}
{animations.length > 0 && (
<select value={currentAnimationId} onChange={(e) => setCurrentAnimationId(e.target.value)}>
{animations.map((anim) => (
<option key={anim.id} value={anim.id}>{anim.id}</option>
))}
</select>
)}
</div>
);
};7. Web Worker for Performance (DotLottieWorker)
import { DotLottieWorker } from '@lottiefiles/dotlottie-web';
// Offload animation rendering to a web worker
new DotLottieWorker({
canvas: document.getElementById('canvas'),
src: 'heavy-animation.lottie',
autoplay: true,
loop: true,
workerId: 'worker-1' // Group multiple animations by worker
});
// Multiple animations in separate workers
new DotLottieWorker({
canvas: document.getElementById('canvas-2'),
src: 'animation-2.lottie',
autoplay: true,
loop: true,
workerId: 'worker-2'
});Integration Patterns
With GSAP ScrollTrigger
import Lottie from 'lottie-react';
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
import animationData from './animation.json';
gsap.registerPlugin(ScrollTrigger);
const GSAPLottieIntegration = () => {
const lottieRef = React.useRef();
React.useEffect(() => {
const anim = lottieRef.current;
if (!anim) return;
// Sync Lottie with scroll
gsap.to(anim, {
scrollTrigger: {
trigger: '#animation-section',
start: 'top center',
end: 'bottom center',
scrub: 1,
onUpdate: (self) => {
const frame = Math.floor(self.progress * (anim.totalFrames - 1));
anim.goToAndStop(frame, true);
}
}
});
}, []);
return (
<div id="animation-section" style={{ height: '200vh' }}>
<Lottie
lottieRef={lottieRef}
animationData={animationData}
autoplay={false}
loop={false}
/>
</div>
);
};With Framer Motion
import { motion } from 'framer-motion';
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
const MotionLottie = () => {
return (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.6 }}
>
<DotLottieReact
src="animation.lottie"
loop
autoplay
style={{ height: 400 }}
/>
</motion.div>
);
};Vue 3 Integration
<script setup>
import { DotLottieVue } from '@lottiefiles/dotlottie-vue';
</script>
<template>
<DotLottieVue
style="height: 500px; width: 500px"
autoplay
loop
src="https://path-to-animation.lottie"
/>
</template>Svelte Integration
<script lang="ts">
import { DotLottieSvelte } from '@lottiefiles/dotlottie-svelte';
import type { DotLottie } from '@lottiefiles/dotlottie-svelte';
let dotLottie: DotLottie | null = null;
function play() {
dotLottie?.play();
}
</script>
<DotLottieSvelte
src="animation.lottie"
loop={true}
autoplay={true}
dotLottieRefCallback={(ref) => dotLottie = ref}
/>
<button on:click={play}>Play</button>Performance Optimization
File Size Optimization
1. Export Settings in After Effects:
- Enable "Skip images that aren't used"
- Use "Glyphs" instead of fonts when possible
- Simplify paths (reduce points in illustrator)
- Avoid effects that create large data (particles, noise)
- Use shape layers instead of vector layers
2. Compression:
- Use dotLottie format (.lottie) for automatic compression
- Run JSON through Lottie optimizer tools
- Remove unnecessary metadata
3. Lazy Loading:
const LazyLottie = () => {
const [shouldLoad, setShouldLoad] = React.useState(false);
React.useEffect(() => {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
setShouldLoad(true);
}
});
observer.observe(document.getElementById('lottie-trigger'));
return () => observer.disconnect();
}, []);
return (
<div id="lottie-trigger">
{shouldLoad && <DotLottieReact src="animation.lottie" loop autoplay />}
</div>
);
};Runtime Performance
1. Renderer Selection:
// SVG: Best quality, slower for complex animations
// Canvas: Better performance, rasterized
// HTML: Limited support, use only for simple animations
// For complex animations, prefer canvas
new DotLottie({
canvas: document.getElementById('canvas'),
src: 'animation.lottie',
autoplay: true,
loop: true,
renderConfig: {
devicePixelRatio: window.devicePixelRatio || 1
}
});2. Web Workers:
// Offload to worker for heavy animations
import { DotLottieWorker } from '@lottiefiles/dotlottie-web';
new DotLottieWorker({
canvas: document.getElementById('canvas'),
src: 'heavy-animation.lottie',
autoplay: true,
loop: true
});3. Mobile Optimization:
// Reduce quality on mobile
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
new DotLottie({
canvas: document.getElementById('canvas'),
src: isMobile ? 'animation-low.lottie' : 'animation-high.lottie',
autoplay: true,
loop: true,
renderConfig: {
devicePixelRatio: isMobile ? 1 : window.devicePixelRatio
}
});Common Pitfalls
1. Memory Leaks from Improper Cleanup
Problem: Not destroying Lottie instances when components unmount.
Solution:
const SafeAnimation = () => {
const [dotLottie, setDotLottie] = React.useState(null);
React.useEffect(() => {
return () => {
// Always destroy instance on unmount
dotLottie?.destroy();
};
}, [dotLottie]);
return <DotLottieReact src="animation.lottie" dotLottieRefCallback={setDotLottie} />;
};2. Event Listener Cleanup
Problem: Event listeners not removed, causing multiple handlers.
Solution:
useEffect(() => {
if (!dotLottie) return;
const handleComplete = () => console.log('Complete');
dotLottie.addEventListener('complete', handleComplete);
// MUST return cleanup function
return () => {
dotLottie.removeEventListener('complete', handleComplete);
};
}, [dotLottie]);3. Large File Sizes
Problem: Exported JSON files are 500KB+ for simple animations.
Solutions:
- Simplify After Effects composition (reduce layers, keyframes)
- Use dotLottie format for compression
- Check Bodymovin export settings (disable "Include expressions" if not needed)
- Remove unused assets before export
- Use Lottie optimizer tools: https://lottiefiles.com/tools/lottie-editor
4. Animation Performance Issues
Problem: Animation stutters or drops frames.
Solutions:
- Switch from SVG to Canvas renderer
- Use
DotLottieWorkerfor web worker rendering - Reduce complexity in After Effects (fewer layers, simpler shapes)
- Lower devicePixelRatio on mobile
- Avoid animating too many properties simultaneously
5. Incorrect Path/URL References
Problem: Animation doesn't load due to CORS or incorrect paths.
Solution:
// Use animationData for local imports (best for bundled apps)
import animationData from './animation.json';
<Lottie animationData={animationData} />
// OR use path for external URLs (requires CORS headers)
<DotLottieReact src="https://example.com/animation.lottie" />
// For Next.js, place in public/ folder
<DotLottieReact src="/animations/animation.lottie" />6. After Effects Export Compatibility
Problem: Some After Effects features don't export to Lottie.
Unsupported features:
- Layer effects (drop shadows, glows) - use shape layers instead
- Blending modes (limited support)
- 3D layers
- Expressions (partial support)
- Track mattes (partial support)
Solution:
- Test export early and often
- Use LottieFiles preview before exporting
- Check Bodymovin compatibility: https://airbnb.io/lottie/#/supported-features
- Convert effects to shapes when possible
Resources
This skill includes:
scripts/
generate_lottie_component.py- Generate React/Vue/Svelte Lottie component boilerplateoptimize_lottie.py- Optimize Lottie JSON file size
references/
api_reference.md- Complete API documentation for lottie-web, lottie-react, and dotlottie-webafter_effects_export.md- Guide for exporting animations from After Effectsperformance_guide.md- Detailed performance optimization strategies
assets/
starter_lottie/- Complete React + Vite starter template with Lottie examplesexamples/- Real-world Lottie animation patterns and use cases
Related Skills
- gsap-scrolltrigger - For scroll-driven Lottie animations synchronized with page scroll
- motion-framer - Combine with Framer Motion for layout animations wrapping Lottie
- animated-component-libraries - Pre-built components that may include Lottie animations
- threejs-webgl - For 3D animations beyond Lottie's 2D capabilities
- react-three-fiber - Alternative for complex 3D animated scenes
Lottie Starter Template
Complete React + Vite starter template with Lottie examples.
Features
- React 18+ with TypeScript support
- Vite for fast development
- DotLottie React integration
- Example animations with controls
- Responsive design
- Production-ready configuration
Installation
# Install dependencies
npm install
# Development
npm run dev
# Build for production
npm run build
# Preview production build
npm run previewProject Structure
starter_lottie/
├── package.json
├── vite.config.js
├── index.html
├── src/
│ ├── main.jsx
│ ├── App.jsx
│ ├── App.css
│ └── components/
│ ├── BasicAnimation.jsx
│ └── InteractiveAnimation.jsx
├── public/
│ └── animations/
│ └── example.lottie
└── README.mdUsage Examples
Basic Animation
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
<DotLottieReact
src="/animations/example.lottie"
loop
autoplay
style={{ height: 400 }}
/>Interactive Animation
const [dotLottie, setDotLottie] = useState(null);
<DotLottieReact
src="/animations/example.lottie"
loop
autoplay={false}
dotLottieRefCallback={setDotLottie}
/>
<button onClick={() => dotLottie?.play()}>Play</button>Configuration
vite.config.js
Optimized for production builds with:
- Code splitting
- Asset optimization
- Gzip compression support
package.json
Includes essential dependencies:
@lottiefiles/dotlottie-reactreactandreact-dom- Development tools
Deployment
Build optimized bundle:
npm run buildOutput in dist/ folder ready for deployment to:
- Vercel
- Netlify
- AWS S3 + CloudFront
- Any static hosting
License
MIT
After Effects to Lottie Export Guide
Complete guide for exporting Lottie animations from After Effects using the Bodymovin plugin.
Prerequisites
1. Adobe After Effects (CC 2015 or later) 2. Bodymovin Plugin - Install from:
- Recommended: AEsc scripts panel (Extension Manager)
- Manual: https://aescripts.com/bodymovin/
- LottieFiles plugin: https://lottiefiles.com/plugins/after-effects
Installation
Method 1: ZXP Installer (Recommended)
1. Download ZXP Installer: https://aescripts.com/learn/zxp-installer/ 2. Download Bodymovin ZXP file 3. Drag ZXP file to ZXP Installer 4. Restart After Effects 5. Open Window → Extensions → Bodymovin
Method 2: Manual Installation
1. Download Bodymovin 2. Extract to:
- Mac:
/Applications/Adobe After Effects [version]/Scripts/ScriptUI Panels/ - Windows:
C:\Program Files\Adobe\Adobe After Effects [version]\Support Files\Scripts\ScriptUI Panels\
3. Restart After Effects
Export Settings
Basic Export
1. Open Window → Extensions → Bodymovin 2. Select composition(s) to export 3. Click "Settings" icon (gear) 4. Configure options (see below) 5. Choose destination folder 6. Click "Render"
Recommended Settings for Web
Essential:
- ✅ Glyphs - Convert text to shapes (reduces file size)
- ✅ Hidden - Skip hidden layers
- ✅ Skip images that aren't used
- ✅ Compress JSON - Minify output (smaller file)
- ⬜ Export Mode: Demo (uncheck for production)
Advanced:
- Export Format: JSON or dotLottie (.lottie)
- dotLottie: Recommended for production (90% smaller)
- Segments: Export specific frame ranges
- Image Quality: 80% (balance size vs quality)
File Size Optimization Settings
✅ Glyphs (text as shapes)
✅ Compress JSON
✅ Skip images that aren't used
✅ Merge Paths (when possible)
⬜ Pretty Print JSON (development only)
⬜ Include in JSON (embed images - increases size)Expected File Sizes:
- Simple icon animation: 5-20 KB
- Complex character animation: 50-150 KB
- Heavy illustration animation: 150-500 KB
- If >500 KB: Optimize composition (see below)
Supported Features
✅ Fully Supported
Shapes:
- Shape layers (rect, ellipse, polygon, star)
- Paths and bezier curves
- Stroke and fill
- Trim paths
- Merge paths
- Repeaters
Transforms:
- Position, scale, rotation, opacity
- Anchor points
- Parenting
Effects:
- Stroke (via shape layers)
- Fill (via shape layers)
- Trim paths
- Merge paths
Masks:
- Shape masks
- Alpha mattes
- Luma mattes (partial)
Text:
- As glyphs (converted to shapes)
- Character animation (requires glyphs)
⚠️ Partial Support
Expressions:
- Simple expressions work
- Complex expressions may fail
- Test thoroughly after export
Blending Modes:
- Limited support
- Normal, Add, Multiply work best
- Others may render incorrectly
Track Mattes:
- Alpha mattes: Good support
- Luma mattes: Limited support
- Inverted mattes: May fail
❌ Not Supported
Layer Effects:
- Drop Shadow → Use shape layer shadow
- Glow → Use shape layer glow
- Bevel & Emboss → Pre-render as image
- All built-in effects → Convert to shapes
3D Layers:
- 3D transforms
- Cameras (except 2D camera moves)
- Lights
Advanced:
- Adjustment layers
- Time remapping
- Frame blending
- Motion blur (partial)
- Gradient strokes (use solid color)
Optimization Techniques
1. Composition Setup
Before Animating:
- Work at 1920x1080 or smaller
- Use round numbers for dimensions
- 30 fps or 60 fps (avoid 23.976, 29.97)
- Keep composition under 5 seconds when possible
Comp Settings:
Width: 1920px (or target size)
Height: 1080px
Frame Rate: 30 fps
Duration: 2-5 seconds2. Shape Layer Optimization
DO:
✅ Use Shape Layers (not vector layers from AI)
✅ Combine shapes with Merge Paths
✅ Use simple gradients (2-3 colors)
✅ Minimize anchor points (simplify paths)
✅ Use solid fills over gradients when possibleDON'T:
❌ Import AI files directly (convert to shapes)
❌ Use too many shapes (>50 layers = slow)
❌ Animate every property (only what's needed)
❌ Use gradient strokes (solid only)3. Image Optimization
Embedded Images:
- Resize to actual displayed size
- Use JPG for photos (not PNG)
- Compress before import (TinyPNG)
- Avoid embedding if possible
Alternative:
- Use shape layers instead
- Pre-render complex elements
- Load images separately in code
4. Text Optimization
Recommended Approach:
1. Create text layer
2. Enable "Glyphs" in Bodymovin settings
3. Text converts to shapes automaticallyManual Alternative:
1. Select text layer
2. Right-click → Create Shapes from Text
3. Delete original text layer
4. Animate shape layersBenefits:
- No font loading required
- Smaller file size
- Guaranteed consistent rendering
5. Animation Optimization
Keyframe Reduction:
// After Effects: Select keyframes
// Right-click → Keyframe Assistant → Reduce Keys
// Threshold: 0.5-1.0 pixelsSimplify Paths:
// Illustrator/After Effects
// Object → Path → Simplify
// Target: 85-90% of original pointsLimit Properties:
Only animate:
Position, Scale, Rotation, Opacity
(Most performant)
Avoid animating:
Anchor Point, Shape Path
(More expensive)Common Export Issues
Issue 1: Huge File Size (>500 KB)
Causes:
- Embedded images
- Too many keyframes
- Complex paths
- Text not converted to glyphs
Solutions:
1. Enable "Glyphs" setting
2. Enable "Skip images that aren't used"
3. Reduce keyframes (Keyframe Assistant → Reduce Keys)
4. Simplify paths in Illustrator
5. Remove unused layers
6. Export as dotLottie formatIssue 2: Animation Doesn't Match Preview
Causes:
- Unsupported features used
- Expression errors
- Blending mode issues
Solutions:
1. Check Bodymovin console for warnings
2. Replace unsupported features
3. Simplify expressions
4. Use Normal blending mode
5. Test export immediatelyIssue 3: Text Rendering Incorrectly
Causes:
- Font not supported
- Glyphs not enabled
- Special characters
Solutions:
1. Enable "Glyphs" in Bodymovin settings
2. OR manually: Create Shapes from Text
3. Avoid special Unicode characters
4. Use web-safe fonts if not using glyphsIssue 4: Colors Look Different
Causes:
- Color space mismatch
- Blending mode issues
- Opacity calculation differences
Solutions:
1. Work in sRGB color space
2. Use Normal blending mode
3. Check opacity values (0-100%)
4. Avoid nested transparencyTesting Workflow
1. Export & Preview
1. Export from After Effects
2. Preview on LottieFiles:
https://lottiefiles.com/preview
3. Check file size (aim for <100 KB)
4. Test on target devices2. Integration Testing
// Quick HTML test
<!DOCTYPE html>
<html>
<body>
<div id="lottie"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js"></script>
<script>
lottie.loadAnimation({
container: document.getElementById('lottie'),
renderer: 'svg',
loop: true,
autoplay: true,
path: 'animation.json'
});
</script>
</body>
</html>3. Performance Testing
// Monitor FPS
const animation = lottie.loadAnimation({...});
let lastTime = performance.now();
let frameCount = 0;
animation.addEventListener('enterFrame', () => {
frameCount++;
const currentTime = performance.now();
if (currentTime - lastTime >= 1000) {
console.log('FPS:', frameCount);
frameCount = 0;
lastTime = currentTime;
}
});
// Target: 60 FPS on desktop, 30 FPS on mobileBest Practices
Pre-Export Checklist
☐ Composition is 1920x1080 or smaller
☐ Frame rate is 30 or 60 fps
☐ Duration is under 10 seconds
☐ All layers are named descriptively
☐ Unused layers are deleted
☐ Text is converted to shapes/glyphs enabled
☐ Images are optimized/removed
☐ No unsupported effects used
☐ Tested in Bodymovin preview
☐ File size is under 100 KB (target)Export Settings Checklist
☐ Format: JSON or dotLottie (.lottie)
☐ Glyphs: Enabled
☐ Hidden: Checked
☐ Skip unused images: Checked
☐ Compress JSON: Enabled
☐ Pretty Print: Disabled (production)
☐ Image Quality: 80%
☐ Export Mode: Standard (not Demo)Post-Export Checklist
☐ Preview on LottieFiles.com
☐ Check file size (<100 KB ideal)
☐ Test in target browser/device
☐ Verify animation timing
☐ Check colors/rendering
☐ Test playback controls
☐ Verify loop behavior
☐ Test on mobile devicesResources
- LottieFiles Preview: https://lottiefiles.com/preview
- Supported Features: https://airbnb.io/lottie/#/supported-features
- Bodymovin Plugin: https://aescripts.com/bodymovin/
- LottieFiles Plugin: https://lottiefiles.com/plugins/after-effects
- File Size Optimizer: https://lottiefiles.com/tools/lottie-editor
Troubleshooting
Bodymovin Panel Not Showing
1. Check installation path is correct
2. Restart After Effects completely
3. Enable "Allow Scripts to Write Files"
(Edit → Preferences → Scripting & Expressions)
4. Reinstall using ZXP InstallerExport Fails/Crashes
1. Update to latest After Effects version
2. Update Bodymovin plugin
3. Simplify composition (remove complex features)
4. Export smaller sections separately
5. Check After Effects error logAnimation Too Large
1. Export as dotLottie (90% smaller)
2. Reduce composition dimensions
3. Simplify paths (fewer anchor points)
4. Remove embedded images
5. Reduce keyframe count
6. Use solid colors over gradientsLottie API Reference
Complete API documentation for lottie-web, lottie-react, dotlottie-web, and dotlottie-react libraries.
dotlottie-web API
DotLottie Constructor
import { DotLottie } from '@lottiefiles/dotlottie-web';
const dotLottie = new DotLottie(config);Config Options:
| Option | Type | Default | Description |
|---|---|---|---|
canvas | HTMLCanvasElement | required | The canvas element to render the animation |
src | string | required | URL to .lottie or .json file |
autoplay | boolean | true | Auto-start animation on load |
loop | boolean | true | Loop animation continuously |
speed | number | 1.0 | Playback speed multiplier (0.5 = half speed, 2 = double speed) |
mode | string | 'forward' | Playback mode: 'forward', 'reverse', 'bounce', 'reverse-bounce' |
backgroundColor | string | null | Canvas background color |
renderConfig | object | {} | Rendering configuration |
data | string \ | ArrayBuffer | null |
marker | string | null | Named marker to play |
segment | [number, number] | null | Frame range to play [startFrame, endFrame] |
useFrameInterpolation | boolean | true | Smooth frame transitions |
Render Config Options:
renderConfig: {
devicePixelRatio: window.devicePixelRatio || 1, // Pixel density
freezeOnOffscreen: false, // Pause when not visible
imageRendering: 'auto' // 'auto', 'crisp-edges', 'pixelated'
}DotLottie Methods
Playback Control:
dotLottie.play(); // Play animation
dotLottie.pause(); // Pause animation
dotLottie.stop(); // Stop and reset to frame 0
dotLottie.setSpeed(speed); // Set playback speed (e.g., 2 for double speed)
dotLottie.setLoop(loop); // Enable/disable looping
dotLottie.setMode(mode); // Set playback mode ('forward', 'reverse', 'bounce')Frame Navigation:
dotLottie.setFrame(frame); // Seek to specific frame (0-indexed)
dotLottie.goToAndPlay(frame, isFrame); // Jump to frame/time and play
dotLottie.goToAndStop(frame, isFrame); // Jump to frame/time and stop
dotLottie.playSegments(segments, forceFlag); // Play frame rangeAnimation Management:
dotLottie.loadAnimation(animationId); // Load specific animation (multi-animation files)
dotLottie.setTheme(themeId); // Set theme (multi-theme files)
dotLottie.resize(); // Resize to canvas dimensions
dotLottie.destroy(); // Cleanup and destroy instanceState Machine (Interactive Lottie):
dotLottie.loadStateMachine(stateMachineId); // Load state machine
dotLottie.startStateMachine(); // Start state machine
dotLottie.stopStateMachine(); // Stop state machine
dotLottie.postStateMachineEvent(event); // Post event to state machineState Machine Events:
dotLottie.postStateMachineEvent("Bool: true");
dotLottie.postStateMachineEvent("Bool: false");
dotLottie.postStateMachineEvent("String: example");
dotLottie.postStateMachineEvent("Numeric: 42.5");
dotLottie.postStateMachineEvent("OnPointerDown: 100 200"); // x, y
dotLottie.postStateMachineEvent("OnPointerUp: 100 200");
dotLottie.postStateMachineEvent("OnPointerMove: 100 200");
dotLottie.postStateMachineEvent("OnPointerEnter: 100 200");
dotLottie.postStateMachineEvent("OnPointerExit: 100 200");
dotLottie.postStateMachineEvent("OnComplete");Layer Information:
const boundingBox = dotLottie.getLayerBoundingBox(layerName);
// Returns: { x, y, width, height } or nullDotLottie Properties
dotLottie.currentFrame; // Current frame number (read-only)
dotLottie.totalFrames; // Total number of frames (read-only)
dotLottie.duration; // Animation duration in seconds (read-only)
dotLottie.isPlaying; // Boolean: is currently playing (read-only)
dotLottie.isLoaded; // Boolean: animation loaded (read-only)
dotLottie.manifest; // Manifest object (animations, themes) (read-only)
dotLottie.activeAnimationId; // Current animation ID (read-only)
dotLottie.activeThemeId; // Current theme ID (read-only)DotLottie Events
// Add event listeners
dotLottie.addEventListener('load', onLoad);
dotLottie.addEventListener('play', onPlay);
dotLottie.addEventListener('pause', onPause);
dotLottie.addEventListener('stop', onStop);
dotLottie.addEventListener('complete', onComplete);
dotLottie.addEventListener('loopComplete', onLoopComplete);
dotLottie.addEventListener('frame', onFrame);
dotLottie.addEventListener('render', onRender);
dotLottie.addEventListener('destroy', onDestroy);
// Remove event listeners
dotLottie.removeEventListener('load', onLoad);Event Callbacks:
function onLoad() {
console.log('Animation loaded');
}
function onPlay() {
console.log('Animation started');
}
function onPause() {
console.log('Animation paused');
}
function onStop() {
console.log('Animation stopped');
}
function onComplete() {
console.log('Animation completed');
}
function onLoopComplete() {
console.log('Loop completed');
}
function onFrame({ currentFrame }) {
console.log('Current frame:', currentFrame);
}
function onRender() {
console.log('Frame rendered');
}
function onDestroy() {
console.log('Instance destroyed');
}---
DotLottieWorker API
Purpose: Offload animation rendering to a Web Worker for better performance.
import { DotLottieWorker } from '@lottiefiles/dotlottie-web';
new DotLottieWorker({
canvas: document.getElementById('canvas'),
src: 'animation.lottie',
autoplay: true,
loop: true,
workerId: 'worker-1' // Optional: group multiple animations by worker
});Config: Same as DotLottie, plus:
| Option | Type | Default | Description |
|---|---|---|---|
workerId | string | auto-generated | Worker ID for grouping animations |
Use Case: Complex animations that cause main thread lag.
---
dotlottie-react API
DotLottieReact Component
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
<DotLottieReact
src="animation.lottie"
loop
autoplay
speed={1}
mode="forward"
backgroundColor="#ffffff"
style={{ height: 400, width: 400 }}
className="lottie-animation"
dotLottieRefCallback={(instance) => setDotLottie(instance)}
animationId="animation-1"
themeId="light"
marker="intro"
segment={[0, 60]}
useFrameInterpolation
data={inlineData}
renderConfig={{ devicePixelRatio: 2 }}
/>Props:
All DotLottie config options are available as props, plus:
| Prop | Type | Description |
|---|---|---|
dotLottieRefCallback | function | Callback to get dotLottie instance |
style | object | Inline styles for wrapper div |
className | string | CSS class for wrapper div |
Getting Instance Reference:
const [dotLottie, setDotLottie] = useState(null);
<DotLottieReact
src="animation.lottie"
dotLottieRefCallback={setDotLottie}
/>
// Use instance
useEffect(() => {
if (dotLottie) {
dotLottie.play();
}
}, [dotLottie]);---
lottie-web API (Original Library)
lottie.loadAnimation()
import lottie from 'lottie-web';
const animation = lottie.loadAnimation({
container: document.getElementById('lottie-container'), // Required
renderer: 'svg', // 'svg', 'canvas', 'html'
loop: true,
autoplay: true,
path: 'animation.json', // URL to animation
// OR
animationData: jsonData, // Inline JSON data
name: 'my-animation', // Optional name
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice',
progressiveLoad: false,
hideOnTransparent: true,
className: 'lottie-svg', // SVG class
scaleMode: 'noScale' // 'noScale', 'fill', 'fit'
}
});lottie-web Methods
Playback:
animation.play();
animation.pause();
animation.stop();
animation.setSpeed(speed); // 1 = normal, 0.5 = half speed
animation.setDirection(direction); // 1 = forward, -1 = reverse
animation.goToAndPlay(frame, isFrame);
animation.goToAndStop(frame, isFrame);
animation.playSegments(segments, forceFlag);Segments:
// Play frames 0-30 only
animation.playSegments([0, 30], false);
// Play multiple segments
animation.playSegments([[0, 30], [60, 90]], false);Properties:
animation.totalFrames;
animation.currentFrame;
animation.frameRate;
animation.isLoaded;
animation.isPaused;
animation.renderer; // 'svg', 'canvas', 'html'
animation.name;Utility:
animation.resize();
animation.setSubframe(useSubframes); // Smooth subframe rendering
animation.getDuration(inFrames); // Duration in frames or seconds
animation.destroy();lottie-web Events
animation.addEventListener('DOMLoaded', onDOMLoaded);
animation.addEventListener('data_ready', onDataReady);
animation.addEventListener('config_ready', onConfigReady);
animation.addEventListener('complete', onComplete);
animation.addEventListener('loopComplete', onLoopComplete);
animation.addEventListener('enterFrame', onEnterFrame);
animation.addEventListener('segmentStart', onSegmentStart);
animation.addEventListener('destroy', onDestroy);
animation.removeEventListener('complete', onComplete);Event Callbacks:
function onEnterFrame(event) {
console.log('Current frame:', event.currentTime);
console.log('Direction:', event.direction); // 1 or -1
}
function onLoopComplete(event) {
console.log('Loop completed:', event.currentLoop);
}lottie Global Methods
lottie.play(name); // Play animation by name
lottie.pause(name); // Pause animation by name
lottie.stop(name); // Stop animation by name
lottie.setSpeed(speed, name); // Set speed for named animation
lottie.setDirection(direction, name);
lottie.destroy(name); // Destroy animation by name
lottie.loadAnimation(config); // Load new animation
lottie.searchAnimations(); // Auto-detect and load animations---
lottie-react API
Lottie Component
import Lottie from 'lottie-react';
import animationData from './animation.json';
<Lottie
animationData={animationData} // Required
loop={true}
autoplay={true}
initialSegment={[0, 60]}
onComplete={onComplete}
onLoopComplete={onLoopComplete}
onEnterFrame={onEnterFrame}
onSegmentStart={onSegmentStart}
onConfigReady={onConfigReady}
onDataReady={onDataReady}
onDataFailed={onDataFailed}
onLoadedImages={onLoadedImages}
onDOMLoaded={onDOMLoaded}
onDestroy={onDestroy}
style={{ height: 400 }}
className="lottie-animation"
lottieRef={lottieRef}
interactivity={interactivityConfig}
/>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
animationData | object | required | Lottie JSON data |
loop | boolean \ | number | true |
autoplay | boolean | true | Auto-start on load |
initialSegment | [number, number] | null | Start frame range |
onComplete | function | null | Callback on animation complete |
onLoopComplete | function | null | Callback on loop complete |
onEnterFrame | function | null | Callback on each frame |
onSegmentStart | function | null | Callback on segment start |
onConfigReady | function | null | Callback on config ready |
onDataReady | function | null | Callback on data ready |
onDataFailed | function | null | Callback on data load failure |
onLoadedImages | function | null | Callback on images loaded |
onDOMLoaded | function | null | Callback on DOM loaded |
onDestroy | function | null | Callback on destroy |
style | object | {} | Inline styles |
className | string | '' | CSS class |
lottieRef | React.RefObject | null | Ref to lottie instance |
interactivity | object | null | Interactivity config (scroll/cursor) |
Using lottieRef
const lottieRef = useRef();
<Lottie animationData={animationData} lottieRef={lottieRef} />
// Access instance
useEffect(() => {
if (lottieRef.current) {
lottieRef.current.setSpeed(2);
lottieRef.current.goToAndPlay(30, true);
}
}, []);Interactivity Config
Scroll Mode:
const interactivity = {
mode: 'scroll',
actions: [
{
visibility: [0, 0.3], // When 0-30% visible
type: 'stop',
frames: [0]
},
{
visibility: [0.3, 1], // When 30-100% visible
type: 'seek',
frames: [0, 60]
}
]
};Cursor Mode:
const interactivity = {
mode: 'cursor',
actions: [
{
position: { x: [0, 1], y: [0, 1] }, // Inside container
type: 'loop',
frames: [0, 60]
},
{
position: { x: -1, y: -1 }, // Outside container
type: 'stop',
frames: [0]
}
]
};Action Types:
'stop'- Stop at specified frame'play'- Play from current frame'loop'- Loop specified segment'seek'- Scrub through frames based on scroll/cursor position
---
useLottie Hook
import { useLottie } from 'lottie-react';
import animationData from './animation.json';
const { View, play, pause, stop, setSpeed, goToAndPlay, goToAndStop } = useLottie({
animationData: animationData,
loop: true,
autoplay: true,
initialSegment: [0, 60]
}, style);Returns:
| Property | Type | Description |
|---|---|---|
View | React.Element | Rendered Lottie component |
play | function | Play animation |
pause | function | Pause animation |
stop | function | Stop animation |
setSpeed | function | Set playback speed |
setDirection | function | Set direction (1 or -1) |
goToAndPlay | function | Jump to frame and play |
goToAndStop | function | Jump to frame and stop |
playSegments | function | Play specific segments |
setSubframe | function | Enable subframe rendering |
getDuration | function | Get animation duration |
destroy | function | Destroy instance |
---
useLottieInteractivity Hook
import { useLottie, useLottieInteractivity } from 'lottie-react';
const lottieObj = useLottie({ animationData });
const Animation = useLottieInteractivity({
lottieObj,
mode: 'scroll', // or 'cursor'
actions: [
{
visibility: [0, 1],
type: 'seek',
frames: [0, 60]
}
]
});
return Animation;---
Best Practices
1. Always destroy instances on unmount:
useEffect(() => {
return () => {
dotLottie?.destroy();
};
}, [dotLottie]);2. Use dotLottie format (.lottie) for production - smaller file sizes
3. Prefer Canvas renderer for complex animations - better performance
4. Use Web Workers (DotLottieWorker) for heavy animations - offload from main thread
5. Lazy load animations - only load when visible (IntersectionObserver)
6. Clean up event listeners:
useEffect(() => {
const handler = () => {};
dotLottie?.addEventListener('complete', handler);
return () => dotLottie?.removeEventListener('complete', handler);
}, [dotLottie]);7. Use animationData prop (not path) for bundled animations - faster, no network request
Lottie Performance Optimization Guide
Comprehensive strategies for optimizing Lottie animation performance in production.
Performance Metrics
Target Performance:
- Desktop: 60 FPS minimum
- Mobile: 30-60 FPS
- File Size: <100 KB ideal, <200 KB acceptable
- Load Time: <500ms for animation ready
- Memory: <50 MB per animation instance
File Size Optimization
1. Use dotLottie Format
// 90% smaller file size
<DotLottieReact src="animation.lottie" /> // ✅ 10-50 KB
// vs
<Lottie animationData={jsonData} /> // ❌ 100-500 KBBenefits:
- ZIP compression (up to 90% reduction)
- Multiple animations in one file
- Theme support
- Faster parsing
2. Optimize JSON
Before Export (After Effects):
- Simplify paths (reduce anchor points)
- Merge shapes when possible
- Remove unused layers/keyframes
- Use solid colors over gradients
- Enable "Compress JSON" in BodymovinAfter Export:
# Use Lottie optimizer
https://lottiefiles.com/tools/lottie-editor
# Or gzip compression (server-side)
gzip animation.json # 60-80% reduction3. Lazy Loading
import { lazy, Suspense } from 'react';
const LottieAnimation = lazy(() => import('./LottieAnimation'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LottieAnimation />
</Suspense>
);
}Intersection Observer Pattern:
const LazyLottie = ({ src }) => {
const [shouldLoad, setShouldLoad] = useState(false);
const ref = useRef();
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setShouldLoad(true);
observer.disconnect();
}
},
{ threshold: 0.1 }
);
if (ref.current) {
observer.observe(ref.current);
}
return () => observer.disconnect();
}, []);
return (
<div ref={ref}>
{shouldLoad ? <DotLottieReact src={src} /> : <div style={{ height: 300 }} />}
</div>
);
};Runtime Performance
1. Renderer Selection
SVG Renderer:
// Best quality, slower performance
// Use for: Simple icons, small animations, few instances
lottie.loadAnimation({
renderer: 'svg', // Vector-based, scalable
container: element,
path: 'animation.json'
});Canvas Renderer:
// Better performance, rasterized
// Use for: Complex animations, many instances, mobile
new DotLottie({
canvas: canvasElement, // ✅ Recommended for complex animations
src: 'animation.lottie'
});Performance Comparison:
| Renderer | Quality | Performance | Use Case |
|---|---|---|---|
| SVG | Excellent | Good (simple animations) | Icons, logos, simple UI |
| Canvas | Good | Excellent | Complex animations, mobile |
| HTML | Poor | Poor | Legacy support only |
2. Web Workers (Best Performance)
import { DotLottieWorker } from '@lottiefiles/dotlottie-web';
// Offload rendering to separate thread
new DotLottieWorker({
canvas: document.getElementById('canvas'),
src: 'heavy-animation.lottie',
autoplay: true,
loop: true,
workerId: 'worker-1'
});Benefits:
- No main thread blocking
- Smooth scroll performance
- Multiple animations without lag
- Better mobile performance
When to use:
- Animation >100 KB
- Multiple simultaneous animations
- Scroll-driven animations
- Mobile devices
3. Device Pixel Ratio Optimization
// Reduce quality on low-end devices
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
const isLowEnd = navigator.deviceMemory < 4; // GB
new DotLottie({
canvas: canvas,
src: 'animation.lottie',
renderConfig: {
devicePixelRatio: (isMobile || isLowEnd) ? 1 : window.devicePixelRatio
// 1 = standard definition (faster)
// 2 = retina (slower, better quality)
}
});4. Animation Complexity Control
Adaptive Quality:
const getAnimationSrc = () => {
const isMobile = /Mobile/i.test(navigator.userAgent);
const isSlowDevice = navigator.hardwareConcurrency < 4;
if (isMobile || isSlowDevice) {
return 'animation-low.lottie'; // Simplified version
}
return 'animation-high.lottie'; // Full quality
};
<DotLottieReact src={getAnimationSrc()} />Frame Rate Reduction:
// Reduce frame rate on slow devices
const targetFPS = isMobile ? 30 : 60;
new DotLottie({
canvas: canvas,
src: 'animation.lottie',
speed: 1,
// Implemented via requestAnimationFrame throttling
});Memory Management
1. Proper Cleanup
const AnimationComponent = () => {
const [dotLottie, setDotLottie] = useState(null);
useEffect(() => {
return () => {
// CRITICAL: Destroy on unmount
dotLottie?.destroy();
};
}, [dotLottie]);
return <DotLottieReact dotLottieRefCallback={setDotLottie} />;
};2. Event Listener Cleanup
useEffect(() => {
if (!dotLottie) return;
const handleComplete = () => console.log('Done');
dotLottie.addEventListener('complete', handleComplete);
// CRITICAL: Remove listeners
return () => {
dotLottie.removeEventListener('complete', handleComplete);
};
}, [dotLottie]);3. Instance Pooling
// Reuse animation instances
class LottiePool {
constructor() {
this.pool = [];
this.active = new Set();
}
acquire(src) {
let instance = this.pool.find(i => i.src === src && !this.active.has(i));
if (!instance) {
instance = new DotLottie({ canvas: createCanvas(), src });
this.pool.push(instance);
}
this.active.add(instance);
return instance;
}
release(instance) {
instance.stop();
this.active.delete(instance);
}
cleanup() {
this.pool.forEach(i => i.destroy());
this.pool = [];
this.active.clear();
}
}Network Optimization
1. CDN Hosting
// Host on CDN for better caching
<DotLottieReact
src="https://cdn.example.com/animations/hero.lottie"
loop
autoplay
/>Benefits:
- Edge caching (faster loads)
- Reduced server load
- Better global performance
2. Preloading Critical Animations
<!-- Preload animation for faster display -->
<link rel="preload" href="/animations/hero.lottie" as="fetch" crossorigin>// Programmatic preload
const preloadAnimation = async (src) => {
const response = await fetch(src);
const blob = await response.blob();
return URL.createObjectURL(blob);
};
// Use preloaded blob
const blobUrl = await preloadAnimation('/animation.lottie');
<DotLottieReact src={blobUrl} />3. Service Worker Caching
// service-worker.js
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('lottie-v1').then((cache) => {
return cache.addAll([
'/animations/hero.lottie',
'/animations/loading.lottie'
]);
})
);
});
self.addEventListener('fetch', (event) => {
if (event.request.url.endsWith('.lottie')) {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
}
});Mobile Optimization
1. Reduce Quality on Mobile
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
<DotLottieReact
src={isMobile ? 'animation-mobile.lottie' : 'animation-desktop.lottie'}
renderConfig={{
devicePixelRatio: isMobile ? 1 : window.devicePixelRatio
}}
/>2. Pause on Offscreen
useEffect(() => {
if (!dotLottie) return;
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
dotLottie.play();
} else {
dotLottie.pause();
}
});
observer.observe(document.getElementById('lottie-container'));
return () => observer.disconnect();
}, [dotLottie]);3. Battery-Aware Performance
// Reduce animation when low battery
navigator.getBattery?.().then((battery) => {
if (battery.level < 0.2) {
dotLottie.setSpeed(0.5); // Slower = less CPU
}
battery.addEventListener('levelchange', () => {
if (battery.level < 0.2) {
dotLottie.stop();
}
});
});Monitoring Performance
1. FPS Monitoring
class FPSMonitor {
constructor() {
this.fps = 0;
this.frames = 0;
this.lastTime = performance.now();
}
update() {
this.frames++;
const currentTime = performance.now();
if (currentTime - this.lastTime >= 1000) {
this.fps = Math.round((this.frames * 1000) / (currentTime - this.lastTime));
console.log(`FPS: ${this.fps}`);
this.frames = 0;
this.lastTime = currentTime;
}
requestAnimationFrame(() => this.update());
}
start() {
this.update();
}
}
const monitor = new FPSMonitor();
monitor.start();2. Memory Usage Tracking
if (performance.memory) {
setInterval(() => {
const used = (performance.memory.usedJSHeapSize / 1048576).toFixed(2);
const total = (performance.memory.jsHeapSizeLimit / 1048576).toFixed(2);
console.log(`Memory: ${used} MB / ${total} MB`);
}, 5000);
}3. Performance Marks
// Mark start
performance.mark('lottie-load-start');
const dotLottie = new DotLottie({...});
dotLottie.addEventListener('load', () => {
// Mark end and measure
performance.mark('lottie-load-end');
performance.measure('lottie-load', 'lottie-load-start', 'lottie-load-end');
const measure = performance.getEntriesByName('lottie-load')[0];
console.log(`Load time: ${measure.duration}ms`);
});Common Performance Issues
Issue 1: Animation Stutters
Symptoms: Dropped frames, jerky playback
Solutions:
1. Switch to Canvas renderer
2. Use DotLottieWorker for web worker rendering
3. Reduce devicePixelRatio (1 instead of 2)
4. Simplify animation (fewer shapes/keyframes)
5. Reduce number of simultaneous animationsIssue 2: Slow Page Load
Symptoms: Long initial render time
Solutions:
1. Use lazy loading with IntersectionObserver
2. Preload critical animations only
3. Use dotLottie format (smaller files)
4. Load animations after page interactive
5. Use code splitting (React.lazy)Issue 3: Memory Leaks
Symptoms: Memory usage increases over time
Solutions:
1. Call destroy() on unmount
2. Remove all event listeners
3. Clear animation references
4. Avoid creating new instances repeatedly
5. Use instance pooling for frequently re-created animationsIssue 4: Mobile Performance Poor
Symptoms: Slow on phones, smooth on desktop
Solutions:
1. Create mobile-optimized version (simpler)
2. Use Canvas renderer (not SVG)
3. Set devicePixelRatio to 1
4. Reduce animation frame rate (30 FPS)
5. Use DotLottieWorker
6. Pause when offscreenBest Practices Summary
File Size:
- ✅ Use dotLottie format (.lottie)
- ✅ Compress JSON
- ✅ Keep under 100 KB
- ✅ Optimize images (or remove)
- ✅ Simplify paths
Runtime:
- ✅ Use Canvas renderer for complex animations
- ✅ Use Web Workers (DotLottieWorker)
- ✅ Destroy instances on unmount
- ✅ Remove event listeners
- ✅ Lazy load animations
Mobile:
- ✅ Create mobile-optimized versions
- ✅ Set devicePixelRatio to 1
- ✅ Pause when offscreen
- ✅ Reduce quality on low-end devices
Monitoring:
- ✅ Track FPS (target 60 on desktop, 30 on mobile)
- ✅ Monitor memory usage
- ✅ Measure load times
- ✅ Test on real devices
#!/usr/bin/env python3
"""
Lottie Component Generator
Generates React, Vue, or Svelte Lottie component boilerplate with common patterns.
Usage:
./generate_lottie_component.py # Interactive mode
./generate_lottie_component.py --framework react --type basic
./generate_lottie_component.py --framework vue --type interactive
"""
import argparse
import sys
TEMPLATES = {
'react_basic': '''import React from 'react';
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
export const {ComponentName} = () => {
return (
<DotLottieReact
src="{animationSrc}"
loop
autoplay
style=\u007b\u007b height: {height}, width: {width} \u007d\u007d
/>
);
};
''',
'react_interactive': '''import React, \u007b useState \u007d from 'react';
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
export const {ComponentName} = () => {
const [dotLottie, setDotLottie] = useState(null);
const handlePlay = () => dotLottie?.play();
const handlePause = () => dotLottie?.pause();
const handleStop = () => dotLottie?.stop();
return (
<div>
<DotLottieReact
src="{animationSrc}"
loop
autoplay=\u007bfalse\u007d
dotLottieRefCallback=\u007bsetDotLottie\u007d
style=\u007b\u007b height: {height}, width: {width} \u007d\u007d
/>
<div style=\u007b\u007b marginTop: 16 \u007d\u007d>
<button onClick=\u007bhandlePlay\u007d>Play</button>
<button onClick=\u007bhandlePause\u007d>Pause</button>
<button onClick=\u007bhandleStop\u007d>Stop</button>
</div>
</div>
);
};
''',
'vue_basic': '''<script setup>
import { DotLottieVue } from '@lottiefiles/dotlottie-vue';
</script>
<template>
<DotLottieVue
src="{animationSrc}"
:autoplay="true"
:loop="true"
:style="{ height: '{height}px', width: '{width}px' }"
/>
</template>
''',
'svelte_basic': '''<script lang="ts">
import { DotLottieSvelte } from '@lottiefiles/dotlottie-svelte';
</script>
<DotLottieSvelte
src="{animationSrc}"
loop=\u007btrue\u007d
autoplay=\u007btrue\u007d
style="height: {height}px; width: {width}px;"
/>
'''
}
def generate_component(framework, component_type, component_name, animation_src, height, width):
"""Generate component code based on parameters."""
key = f"{framework}_{component_type}"
if key not in TEMPLATES:
print(f"Error: Template '{key}' not found")
sys.exit(1)
template = TEMPLATES[key]
code = template.format(
ComponentName=component_name,
animationSrc=animation_src,
height=height,
width=width
)
return code
def interactive_mode():
"""Run interactive mode to gather component parameters."""
print("Lottie Component Generator")
print("-" * 40)
# Framework selection
print("\nSelect framework:")
print("1. React")
print("2. Vue")
print("3. Svelte")
framework_choice = input("Enter choice (1-3): ").strip()
framework_map = {'1': 'react', '2': 'vue', '3': 'svelte'}
framework = framework_map.get(framework_choice, 'react')
# Component type
if framework == 'react':
print("\nSelect component type:")
print("1. Basic (just displays animation)")
print("2. Interactive (with playback controls)")
type_choice = input("Enter choice (1-2): ").strip()
component_type = 'interactive' if type_choice == '2' else 'basic'
else:
component_type = 'basic'
# Component details
component_name = input("\nComponent name (e.g., HeroAnimation): ").strip() or "LottieAnimation"
animation_src = input("Animation source URL or path: ").strip() or "/animations/animation.lottie"
height = input("Height in pixels (default 400): ").strip() or "400"
width = input("Width in pixels (default 400): ").strip() or "400"
code = generate_component(framework, component_type, component_name, animation_src, height, width)
# Output
extensions = {'react': 'jsx', 'vue': 'vue', 'svelte': 'svelte'}
filename = f"{component_name}.{extensions[framework]}"
print(f"\nGenerated {filename}:")
print("-" * 40)
print(code)
save = input("\nSave to file? (y/n): ").strip().lower()
if save == 'y':
with open(filename, 'w') as f:
f.write(code)
print(f"✅ Saved to {filename}")
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(description="Generate Lottie component boilerplate")
parser.add_argument('--framework', choices=['react', 'vue', 'svelte'], help="Framework to use")
parser.add_argument('--type', choices=['basic', 'interactive'], default='basic', help="Component type")
parser.add_argument('--name', default='LottieAnimation', help="Component name")
parser.add_argument('--src', default='/animations/animation.lottie', help="Animation source")
parser.add_argument('--height', default='400', help="Height in pixels")
parser.add_argument('--width', default='400', help="Width in pixels")
parser.add_argument('--output', help="Output file path")
args = parser.parse_args()
# Interactive mode if no framework specified
if not args.framework:
interactive_mode()
return
code = generate_component(
args.framework,
args.type,
args.name,
args.src,
args.height,
args.width
)
if args.output:
with open(args.output, 'w') as f:
f.write(code)
print(f"✅ Generated {args.output}")
else:
print(code)
if __name__ == '__main__':
main()
#!/usr/bin/env python3
"""
Lottie JSON Optimizer
Optimizes Lottie JSON files by removing whitespace, rounding numbers, and removing unnecessary data.
Usage:
./optimize_lottie.py animation.json # Output to stdout
./optimize_lottie.py animation.json -o optimized.json
"""
import json
import argparse
import sys
def round_number(value, precision=2):
"""Round number to specified precision."""
if isinstance(value, (int, float)):
return round(value, precision)
return value
def optimize_object(obj, precision=2):
"""Recursively optimize object by rounding numbers."""
if isinstance(obj, dict):
return {k: optimize_object(v, precision) for k, v in obj.items()}
elif isinstance(obj, list):
return [optimize_object(item, precision) for item in obj]
elif isinstance(obj, float):
return round(obj, precision)
return obj
def optimize_lottie(input_path, output_path=None, precision=2):
"""Optimize Lottie JSON file."""
try:
# Read input
with open(input_path, 'r') as f:
data = json.load(f)
# Optimize
optimized = optimize_object(data, precision)
# Write output
json_str = json.dumps(optimized, separators=(',', ':'))
if output_path:
with open(output_path, 'w') as f:
f.write(json_str)
print(f"✅ Optimized: {input_path} → {output_path}")
# Show size reduction
import os
original_size = os.path.getsize(input_path)
optimized_size = os.path.getsize(output_path)
reduction = ((original_size - optimized_size) / original_size) * 100
print(f" Original: {original_size:,} bytes")
print(f" Optimized: {optimized_size:,} bytes")
print(f" Reduction: {reduction:.1f}%")
else:
print(json_str)
except FileNotFoundError:
print(f"Error: File '{input_path}' not found", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON - {e}", file=sys.stderr)
sys.exit(1)
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(description="Optimize Lottie JSON files")
parser.add_argument('input', help="Input Lottie JSON file")
parser.add_argument('-o', '--output', help="Output file path")
parser.add_argument('-p', '--precision', type=int, default=2, help="Number precision (default: 2)")
args = parser.parse_args()
optimize_lottie(args.input, args.output, args.precision)
if __name__ == '__main__':
main()
Related skills
How it compares
Use lottie-animations when you want a Vite React starter with DotLottie wired in rather than manually installing animation libraries from scratch.
FAQ
What does lottie-animations do?
After Effects animation rendering for web and React applications. Use this skill when implementing Lottie animations, JSON vector animations, interactive animated icons, micro-interactions, or loading
When should I use lottie-animations?
During build integrations work for generative media.
Is lottie-animations safe to install?
Review the Security Audits panel on this listing before production use.