---
name: frontend-lottie
description: Decorative JSON animations for UI feedback and polish. Use for loading spinners, success/error checkmarks, empty state illustrations, animated icons. Just plays and loops - no interactivity. For reactive/stateful animations use Rive instead. Lightweight and SSR-compatible.
allowed-tools: Read, Edit, Write, Bash (*)
---
# Lottie
Lightweight vector animations. Just plays and loops — no complex logic.
## When to Use
- Loading spinners
- Success/error checkmarks
- Empty state illustrations
- Decorative micro-animations
- Animated icons
## When NOT to Use
- Animation reacts to input → Rive
- Multiple states/transitions → Rive
- Complex interactivity → Rive
## Process
**FIND → ADD → INTEGRATE**
1. Find animation: https://lottiefiles.com
2. Download .lottie or .json
3. Place in `public/animations/`
4. Use component
## Quick Start
```bash
npm install @lottiefiles/dotlottie-react
```
```tsx
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
// Simple autoplay
```
## Common Patterns
```tsx
// Loading spinner
// Success feedback (plays once)
// Empty state
No results found
// Loading button
{isLoading ? (
) : (
"Submit"
)}
```
## Decision: Lottie vs Rive
| Animation Type | Use |
|----------------|-----|
| Just plays/loops | Lottie ✓ |
| Reacts to hover/click | Rive |
| State machine | Rive |
| Data-driven | Rive |
| Simple loader | Lottie ✓ |
## Finding Animations
```yaml
LottieFiles: https://lottiefiles.com/free-animations
Lordicon: https://lordicon.com (animated icons)
useAnimations: https://useanimations.com (micro-interactions)
```
## Playback Control
```tsx
'use client'
import { useState, useCallback } from 'react'
import { DotLottieReact, DotLottie } from '@lottiefiles/dotlottie-react'
function ControlledLottie() {
const [dotLottie, setDotLottie] = useState(null)
return (
dotLottie?.play()}
onMouseLeave={() => dotLottie?.pause()}
>
)
}
// Methods: play(), pause(), stop(), setSpeed(n), goToAndPlay(frame)
```
## SSR & Hydration
```tsx
// Always 'use client'
'use client'
// Dynamic import
const DotLottieReact = dynamic(
() => import('@lottiefiles/dotlottie-react').then(m => m.DotLottieReact),
{ ssr: false }
)
// Or mounted check
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
if (!mounted) return
```
## Performance
```tsx
// Pause when not visible
import { useInView } from 'react-intersection-observer'
const { ref, inView } = useInView({ threshold: 0.1 })
useEffect(() => {
inView ? dotLottie?.play() : dotLottie?.pause()
}, [inView, dotLottie])
```
## File Structure
```
public/animations/
loaders/spinner.lottie
feedback/success.lottie, error.lottie
empty-states/no-data.lottie
illustrations/hero.lottie
```
## Troubleshooting
```yaml
"Animation not loading":
→ Check file path in public/
→ Verify .lottie or .json extension
"Animation not playing":
→ Add autoplay={true}
→ Add loop={true}
"Hydration mismatch":
→ Add 'use client'
→ Use dynamic(() => ..., { ssr: false })
"Too fast/slow":
→ speed={0.5} for slower
→ speed={2} for faster
```
## References
- **[patterns.md](references/patterns.md)** — Controlled playback, events, visibility pause, hover/click triggers
## External Resources
- https://lottiefiles.com/free-animations — Free animations
- https://useanimations.com — Micro-interactions
- https://lordicon.com — Animated icons
- For latest API → use context7 skill