DEV Community

Fazal Shah
Fazal Shah

Posted on

Lottie vs Framer Motion: Which Should You Use?

Both Lottie and Framer Motion are popular animation tools for web apps, but they solve completely different problems. Using the wrong one will make your life harder. Here's how to pick.


The Core Difference

Lottie plays pre-built animations exported from a design tool (After Effects, Lottie Editor, etc.). The motion is defined entirely by the designer.

Framer Motion is a React animation library for animating DOM elements in code. The motion is defined entirely by the developer.

This isn't a competition — they're for different jobs.


When to Use Lottie

Use Lottie when:

  • The animation was created in After Effects, LottieFiles Editor, or similar
  • You need complex illustrated motion (characters, particles, morphing shapes)
  • The animation has a fixed, pre-designed visual style that must be pixel-perfect
  • You need micro-interactions from an icon library (animated icons, loading states)
  • File size is critical (convert to .lottie for 75% smaller — verify at IconKing)

Best use cases:

  • Hero animations and brand illustrations
  • Animated onboarding flows
  • Loading spinners and success animations
  • Animated icon sets
  • Complex path animations

When to Use Framer Motion

Use Framer Motion when:

  • You need to animate React components based on state changes
  • You need layout animations (elements moving position when others appear/disappear)
  • You need gesture-based animations (drag, hover, tap)
  • You need spring physics and momentum
  • You need orchestrated sequences across multiple components
  • You need scroll-based progress animations tied to scroll position

Best use cases:

  • Page transitions
  • Modal/drawer open-close animations
  • Drag-and-drop interfaces
  • Accordion/collapse animations
  • List item add/remove animations
  • Hover effects on UI components

Side-by-Side Comparison

Feature Lottie Framer Motion
Animation source Designer file (After Effects) Code
Complexity ceiling Very high (full AE support) Medium (CSS-level complexity)
Designer control 100% None
Developer control Limited (play/pause/speed) 100%
File size 10–200KB per animation ~45KB library
Bundle impact Per-animation One-time overhead
State-driven animation Limited Excellent
Gesture support None Built-in
Layout animation None Built-in
SSR support Needs guards Native

Bundle Size Reality

Lottie:

  • lottie-web: ~240KB gzipped (full build), ~150KB (light build)
  • @lottiefiles/dotlottie-web: ~50KB gzipped
  • Per animation: 5–200KB depending on complexity (use .lottie for 75% reduction)

Framer Motion:

  • Full bundle: ~45KB gzipped
  • Motion-safe subset (motion/react): ~17KB gzipped

If you only need a few simple loading animations, Lottie with dotLottie format can actually be lighter. If you need rich UI animations throughout your app, Framer Motion's single bundle overhead is more efficient.


Code Comparison

Loading Spinner

With Lottie:

import { DotLottieReact } from '@lottiefiles/dotlottie-react';

// Designer created this — developer just plays it
function Spinner() {
  return (
    <div role="status" aria-label="Loading">
      <DotLottieReact src="/animations/spinner.lottie" loop autoplay style={{ width: 48, height: 48 }} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

With Framer Motion:

import { motion } from 'framer-motion';

// Developer defines all motion in code
function Spinner() {
  return (
    <motion.div
      role="status"
      aria-label="Loading"
      style={{ width: 48, height: 48, borderRadius: '50%', border: '3px solid #e0e0e0', borderTop: '3px solid blue' }}
      animate={{ rotate: 360 }}
      transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Modal Animation

With Lottie: You can't — Lottie doesn't animate DOM layout changes.

With Framer Motion:

import { AnimatePresence, motion } from 'framer-motion';

function Modal({ isOpen, onClose, children }) {
  return (
    <AnimatePresence>
      {isOpen && (
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: 20 }}
          transition={{ duration: 0.2 }}
          style={{ position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }}
        >
          {children}
        </motion.div>
      )}
    </AnimatePresence>
  );
}
Enter fullscreen mode Exit fullscreen mode

Animated Icon (Hover State)

With Lottie (best choice for complex icons):

import Lottie from 'lottie-react';
import { useRef } from 'react';
import heartAnim from '/animations/heart.json';

function HeartButton() {
  const lottieRef = useRef(null);

  return (
    <button
      onMouseEnter={() => lottieRef.current?.play()}
      onMouseLeave={() => lottieRef.current?.stop()}
      aria-label="Like"
    >
      <div aria-hidden="true" style={{ width: 32, height: 32 }}>
        <Lottie lottieRef={lottieRef} animationData={heartAnim} loop={false} autoplay={false} />
      </div>
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

With Framer Motion (simpler icons):

import { motion } from 'framer-motion';

function HeartButton() {
  return (
    <motion.button
      whileHover={{ scale: 1.2 }}
      whileTap={{ scale: 0.9 }}
      aria-label="Like"
    >
      <svg>/* heart SVG path */</svg>
    </motion.button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Using Both Together

The best setups often use both:

import { motion, AnimatePresence } from 'framer-motion';
import { DotLottieReact } from '@lottiefiles/dotlottie-react';

function FormSubmit({ status }) {
  return (
    // Framer Motion handles the container animation
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      {status === 'loading' && (
        // Lottie handles the complex spinner animation
        <DotLottieReact src="/animations/spinner.lottie" loop autoplay style={{ width: 48, height: 48 }} />
      )}
      {status === 'success' && (
        // Lottie plays the success animation
        <DotLottieReact src="/animations/success.lottie" loop={false} autoplay style={{ width: 48, height: 48 }} />
      )}
    </motion.div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Use Framer Motion for UI state transitions. Use Lottie for the illustrations inside those transitions.


Decision Tree

Is the animation a pre-built file from a designer?
├── YES → Use Lottie
└── NO → Do you need layout animation, gestures, or spring physics?
          ├── YES → Use Framer Motion
          └── NO → Are you animating existing DOM elements?
                    ├── YES → Use Framer Motion (or CSS transitions)
                    └── NO → You probably need Lottie + a designer
Enter fullscreen mode Exit fullscreen mode

Quick Take

  • Complex illustrated animations, brand motion, icon animations → Lottie
  • UI transitions, gestures, layout animations, state-driven motion → Framer Motion
  • Both → Use them together; they're complementary, not competing

Before committing to any Lottie file: preview it at IconKing — verify colors, timing, and convert to .lottie format for the smallest possible file size.

Top comments (0)