DEV Community

Fazal Shah
Fazal Shah

Posted on

Lottie Animation Performance: The Complete Optimization Guide (2025)

Lottie animations are efficient by default — but they can still tank performance if you're not careful. This guide covers every optimization technique: renderer selection, off-screen pausing, file size reduction, memory management, and debugging.


Why Performance Matters for Lottie

A single Lottie animation running off-screen wastes CPU/GPU. Ten simultaneous animations can cause frame drops. A 500KB JSON file delays LCP. These problems compound on mobile, where batteries drain and memory is limited.

The good news: most Lottie performance issues are fixable in under 10 minutes.


Step 0: Audit Your Files First

Before optimizing code, audit the animation files themselves. Open each .json or .lottie file in IconKing:

  • Check file size and layer count
  • Identify unsupported effects (Gaussian blur, 3D layers) that force fallback rendering
  • Convert .json → .lottie for 75% file size reduction
  • Verify the animation renders correctly before adding it to your build

Small file → less parsing time → faster first paint.


1. Choose the Right Renderer

Lottie supports three renderers: SVG, Canvas, and HTML. The choice has a major performance impact.

SVG (default)

  • Creates DOM nodes for every animated element
  • Best quality, highest compatibility
  • Use for: 1–5 simultaneous animations, complex paths, sharp edges at all sizes

Canvas

  • Renders to a single bitmap — no DOM overhead
  • Significantly faster for many simultaneous animations
  • Use for: 6+ simultaneous animations, lists/grids of animated items, game-like UIs

HTML

  • CSS-based, very limited feature support
  • Fastest for simple animations, but most Lottie files break
  • Use for: Only the simplest animations with CSS-compatible properties
// Switch to Canvas for performance-critical scenarios
const anim = lottie.loadAnimation({
  container: el,
  renderer: 'canvas',  // ← change this
  loop: true,
  autoplay: true,
  path: '/animation.json'
});
Enter fullscreen mode Exit fullscreen mode

Rule of thumb: Use SVG until you notice frame drops. Switch to Canvas if rendering 5+ animations simultaneously.


2. Pause Off-Screen Animations (IntersectionObserver)

Running animations consume CPU even when invisible. Always use IntersectionObserver to pause/play based on visibility:

const anim = lottie.loadAnimation({ /* ... */ });

const observer = new IntersectionObserver(([entry]) => {
  entry.isIntersecting ? anim.play() : anim.pause();
}, { threshold: 0.1 });

observer.observe(document.getElementById('anim-container'));
Enter fullscreen mode Exit fullscreen mode

React:

import { useRef, useEffect } from 'react';
import Lottie, { LottieRefCurrentProps } from 'lottie-react';

export function IntersectionLottie({ animationData }) {
  const containerRef = useRef(null);
  const lottieRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      entry.isIntersecting ? lottieRef.current?.play() : lottieRef.current?.pause();
    }, { threshold: 0.1 });

    if (containerRef.current) observer.observe(containerRef.current);
    return () => observer.disconnect();
  }, []);

  return (
    <div ref={containerRef}>
      <Lottie lottieRef={lottieRef} animationData={animationData} autoplay={false} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Impact: On a page with 8 animations in a long scroll, pausing off-screen ones reduces CPU usage by 60–80%.


3. Use .lottie Format (75% Smaller Files)

The .lottie (dotLottie) format compresses animations significantly over plain JSON:

Format Typical Size Load Time (3G)
.json 80KB 320ms
.lottie 20KB 80ms

Convert at IconKing — drop the file, click convert, download. Then switch to @lottiefiles/dotlottie-web:

npm install @lottiefiles/dotlottie-web
# or React
npm install @lottiefiles/dotlottie-react
Enter fullscreen mode Exit fullscreen mode
import { DotLottieReact } from '@lottiefiles/dotlottie-react';

<DotLottieReact
  src="/animations/loading.lottie"
  loop
  autoplay
/>
Enter fullscreen mode Exit fullscreen mode

Impact: Faster LCP, better Core Web Vitals Lighthouse scores, less bandwidth on mobile.


4. Lazy Load Animation Files

Don't bundle large animation files with your JavaScript. Load them on demand:

// Bad: bundled in JS
import animData from './heavy-animation.json'; // added to main bundle

// Good: loaded at runtime
async function loadAnim(container) {
  const response = await fetch('/animations/heavy.json');
  const animData = await response.json();
  return lottie.loadAnimation({ container, animationData: animData, renderer: 'svg', loop: true, autoplay: true });
}
Enter fullscreen mode Exit fullscreen mode

React with Next.js dynamic import:

import dynamic from 'next/dynamic';

const HeavyAnimation = dynamic(
  () => import('@/components/HeavyAnimation'),
  { 
    ssr: false,
    loading: () => <div className="w-48 h-48 bg-gray-100 animate-pulse rounded-lg" />
  }
);
Enter fullscreen mode Exit fullscreen mode

Impact: Reduces initial JS bundle size. Critical for First Contentful Paint.


5. Preload Critical Above-the-Fold Animations

For hero animations that appear immediately, preload them:

<link rel="preload" href="/animations/hero.lottie" as="fetch" crossorigin>
Enter fullscreen mode Exit fullscreen mode

Or in Next.js <Head>:

import Head from 'next/head';

<Head>
  <link rel="preload" href="/animations/hero.lottie" as="fetch" crossOrigin="anonymous" />
</Head>
Enter fullscreen mode Exit fullscreen mode

Impact: Eliminates the flash of a missing animation on initial load.


6. Limit Simultaneous Animations

Each running animation runs its own requestAnimationFrame loop. On mobile:

  • 1–3 animations: negligible impact
  • 4–8 animations: noticeable battery drain
  • 10+ animations: frame drops likely

Strategies:

  • Stagger animation starts so not all fire at once
  • Use lottie.pause() to pause all animations when the tab is hidden
  • Use IntersectionObserver aggressively — only play what's visible
// Pause all when tab hidden
document.addEventListener('visibilitychange', () => {
  document.hidden ? lottie.pause() : lottie.play();
});
Enter fullscreen mode Exit fullscreen mode

7. Reduce Canvas Size

A 1000×1000 Canvas animation rendered in a 50×50 box wastes GPU. Set the container to the actual display size:

const anim = lottie.loadAnimation({
  container: el,
  renderer: 'canvas',
  rendererSettings: {
    clearCanvas: true,
    // Don't render at 2x unnecessarily
    dpr: 1 // override devicePixelRatio if retina isn't needed
  }
});
Enter fullscreen mode Exit fullscreen mode

For SVG renderer, don't scale a 400×400 animation down to 24×24 via CSS — export a smaller version from After Effects.


8. Destroy Animations You No Longer Need

Idle Lottie instances consume memory. Always destroy:

// Vanilla JS
anim.destroy();

// React — cleanup in useEffect
useEffect(() => {
  const anim = lottie.loadAnimation({ /* ... */ });
  return () => anim.destroy();
}, []);
Enter fullscreen mode Exit fullscreen mode

In SPAs (React, Vue, Angular), this is critical. Route changes don't automatically clean up DOM-based animations.


9. Use Progressive Loading

For complex animations, use progressiveLoad: true to start rendering before the full file has parsed:

lottie.loadAnimation({
  container: el,
  renderer: 'svg',
  rendererSettings: {
    progressiveLoad: true
  },
  path: '/animations/complex.json'
});
Enter fullscreen mode Exit fullscreen mode

Impact: Animation appears sooner, even for large files.


10. Simplify Complex Animations at the Source

If an animation is still slow after all of the above, the issue is in the After Effects file itself:

  • Too many layers: Ask your designer to merge/flatten layers
  • Unsupported effects: Gaussian blur, drop shadows, certain blend modes force CPU fallback
  • Raster images embedded: Dramatic file size increase; use vector shapes instead
  • Very high frame rate: 60fps animations are 2× larger than 30fps; request 24–30fps for most use cases

Open the file in IconKing to inspect layer count and check for rendering issues before sending the designer a revision request.


Quick Audit Checklist

Check Action
File format is .json Convert to .lottie at IconKing
Animation runs off-screen Add IntersectionObserver pause/play
Many simultaneous animations Switch to Canvas renderer
Animation in SPA Add destroy() in component cleanup
Hero animation delays LCP Add <link rel="preload">
File size > 100KB Simplify at source or split animation
Tab hidden = animation running Add visibilitychange listener

Measuring Performance

Use Chrome DevTools Performance tab to profile:

  1. Record a 5-second window with animations running
  2. Look for long paint/render tasks in the main thread
  3. Check GPU layers — excessive layer promotion indicates over-animation

For Core Web Vitals, Lighthouse will flag LCP issues from large animation files.


Summary

  1. File format: Convert to .lottie at IconKing for 75% smaller files
  2. Renderer: SVG for quality, Canvas for 6+ simultaneous animations
  3. Visibility: IntersectionObserver pause/play on every animation
  4. Loading: Lazy load large files; preload critical above-fold ones
  5. Memory: Destroy animations in component cleanup
  6. Tab: Pause all when document.hidden
  7. Source: Work with designers to reduce layer count and remove unsupported effects

Top comments (0)