Lottie animations are beautiful — but they can balloon your page weight if you're not careful. A single complex animation can easily hit 500KB or more, which will tank your Core Web Vitals.
This guide covers every technique to reduce Lottie file size, with real numbers.
Why Lottie Files Get Large
Lottie JSON is verbose. Every keyframe, every shape, every layer property gets encoded. A 5-second animation from After Effects might export with:
- Thousands of redundant keyframes
- Unused layers and precomps
- Embedded image assets (base64-encoded, inside the JSON)
- Shapes that could be simplified
The result: files that are 10–100x larger than they need to be.
Method 1: Preview and Inspect Before You Optimize
Before touching anything, you need to understand what you're dealing with.
Load your animation in IconKing — it's a free browser tool that lets you:
- Preview
.jsonand.lottiefiles instantly - See layer structure and complexity
- Spot embedded images that are inflating file size
No upload, no account. Just drop the file and inspect.
Method 2: Convert to dotLottie Format
The single biggest win: convert .json to .lottie (dotLottie format).
What is dotLottie? It's a ZIP-compressed container for Lottie animations. The same animation that's 200KB as JSON is typically 50–80KB as .lottie.
How to convert: Use IconKing's converter — drop your .json, click convert, download .lottie. Takes 5 seconds.
dotLottie is supported by:
-
@lottiefiles/dotlottie-web @lottiefiles/dotlottie-react- LottieFiles player v2+
If you're still serving raw .json in 2025, you're leaving 60–80% size reduction on the table.
Method 3: Remove Unused Layers
Open your animation in After Effects (or ask your designer to) and:
- Look for layers that are invisible for the entire animation
- Delete precomps that aren't referenced
- Remove guide layers and reference artwork
A 400KB animation often has 100KB+ of hidden/unused content.
Method 4: Reduce Keyframe Density
After Effects can export animations with a keyframe on every single frame (25–60 keyframes per second). Most of those intermediate keyframes are unnecessary — Lottie interpolates between them anyway.
In After Effects:
- Open the Bodymovin plugin
- Check "Glyphs" → "Merge paths"
- Reduce "Round corners" precision
Or run the JSON through the lottie-optimizer npm package:
npm install -g lottie-optimizer
lottie-optimizer input.json output.json --quality 80
Typical result: 30–50% file size reduction with no visible quality difference.
Method 5: Remove Embedded Images
Lottie files sometimes contain base64-encoded images inside the JSON. These are the worst offenders — a single embedded PNG can add 300KB+.
To detect them:
grep -o '"t":0' your-animation.json | wc -l
# Or just look for very long strings starting with "data:image"
To fix: In the Bodymovin export settings, choose "External images" instead of embedded. Host the images separately. The Lottie JSON will reference them by filename.
Method 6: Simplify Paths
Complex vector shapes export with extreme precision — 6+ decimal places on every bezier control point. You don't need that.
Use this snippet to round coordinates in your Lottie JSON:
function roundLottieCoords(obj, precision = 2) {
if (typeof obj === 'number') {
return parseFloat(obj.toFixed(precision));
}
if (Array.isArray(obj)) {
return obj.map(item => roundLottieCoords(item, precision));
}
if (typeof obj === 'object' && obj !== null) {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, roundLottieCoords(v, precision)])
);
}
return obj;
}
// Usage:
const lottie = JSON.parse(fs.readFileSync('animation.json', 'utf8'));
const optimized = roundLottieCoords(lottie, 2);
fs.writeFileSync('animation-optimized.json', JSON.stringify(optimized));
Typical savings: 10–25% with no visual change.
Method 7: Lazy Load Animations
Even a well-optimized Lottie shouldn't block page load. Use Intersection Observer to only load animations when they scroll into view:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadAnimation(entry.target);
observer.unobserve(entry.target);
}
});
}, { rootMargin: '200px' });
document.querySelectorAll('[data-lottie]').forEach(el => {
observer.observe(el);
});
function loadAnimation(el) {
const src = el.dataset.lottie;
lottie.loadAnimation({
container: el,
renderer: 'svg',
loop: true,
autoplay: true,
path: src
});
}
Method 8: Use the SVG Renderer (Not Canvas)
The svg renderer produces crisp animations at any resolution with no rasterization. The canvas renderer forces a fixed pixel density. For most use cases, svg performs better and looks better.
lottie.loadAnimation({
renderer: 'svg', // not 'canvas'
...
});
Checklist: Before You Ship a Lottie Animation
- [ ] Previewed in IconKing to check for hidden layers or embedded images
- [ ] Converted to
.lottie(dotLottie) format — or confirmed.jsonis under 50KB - [ ] Lazy-loaded (not blocking page render)
- [ ] No base64-encoded images inside the JSON
- [ ] Keyframe density reduced if animation was exported at full frame rate
Real Numbers
Here's what these techniques achieved on a typical hero animation (180KB original):
| Technique | Size After | Savings |
|---|---|---|
Original .json
|
180KB | — |
| Remove unused layers | 140KB | 22% |
Convert to .lottie
|
45KB | 75% |
| Round coordinates | 40KB | 78% |
| Lazy load | 40KB | No size change, 100ms faster LCP |
Final result: 40KB, loads only when visible. That's the target.
Tools Referenced
- IconKing — free browser tool to preview, inspect, and convert Lottie animations (no upload required)
- lottie-optimizer — npm package for keyframe reduction
- @lottiefiles/dotlottie-web — dotLottie player
Have a Lottie optimization technique I missed? Drop it in the comments.
Top comments (0)