Frontend Development

Mastering Animations in React with Framer Motion

Create stunning, performant animations in your React applications with Framer Motion. From basic transitions to complex gesture-based interactions.

N

Neha Verma

Frontend Developer

February 28, 2025

9 min read

#React#Animation#Framer Motion#UI/UX
Mastering Animations in React with Framer Motion

Mastering Animations in React with Framer Motion

Animations breathe life into web applications, transforming static interfaces into engaging experiences. Framer Motion has emerged as the go-to animation library for React, offering powerful features with an intuitive API.

Why Framer Motion?

Traditional CSS animations and JavaScript-based libraries often require complex code for simple effects. Framer Motion simplifies this:

  • Declarative syntax: Animations as props
  • Performance: Hardware-accelerated by default
  • Gestures: Built-in drag, tap, and hover support
  • Spring physics: Natural, fluid motion
  • Layout animations: Automatic smooth transitions

Getting Started

Installation

npm install framer-motion

Basic Animation

import { motion } from "framer-motion";

export default function FadeIn() {
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.6 }}
    >
      Content fades in
    </motion.div>
  );
}

Essential Animation Patterns

1. Entrance Animations

Create engaging page load experiences:

export default function Hero() {
  return (
    <div>
      <motion.h1
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.6, delay: 0.2 }}
      >
        Welcome to Star Works
      </motion.h1>

      <motion.p
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.6, delay: 0.4 }}
      >
        Transform your digital presence
      </motion.p>

      <motion.button
        initial={{ opacity: 0, scale: 0.8 }}
        animate={{ opacity: 1, scale: 1 }}
        transition={{ duration: 0.4, delay: 0.6 }}
        whileHover={{ scale: 1.05 }}
        whileTap={{ scale: 0.95 }}
      >
        Get Started
      </motion.button>
    </div>
  );
}

2. Scroll-Triggered Animations

Animate elements as they enter the viewport:

export default function ScrollReveal() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 50 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true, amount: 0.3 }}
      transition={{ duration: 0.6 }}
    >
      This appears when scrolled into view
    </motion.div>
  );
}

3. Staggered Animations

Create cascading effects for lists:

const container = {
  hidden: { opacity: 0 },
  show: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1,
    },
  },
};

const item = {
  hidden: { opacity: 0, y: 20 },
  show: { opacity: 1, y: 0 },
};

export default function StaggeredList() {
  return (
    <motion.ul variants={container} initial="hidden" animate="show">
      {items.map((item, i) => (
        <motion.li key={i} variants={item}>
          {item}
        </motion.li>
      ))}
    </motion.ul>
  );
}

4. Interactive Hover Effects

export default function InteractiveCard() {
  return (
    <motion.div
      className="card"
      whileHover={{
        scale: 1.05,
        boxShadow: "0 10px 40px rgba(195, 240, 15, 0.3)",
      }}
      whileTap={{ scale: 0.98 }}
      transition={{ type: "spring", stiffness: 300 }}
    >
      Hover over me
    </motion.div>
  );
}

5. Drag Interactions

export default function DraggableElement() {
  return (
    <motion.div
      drag
      dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}
      dragElastic={0.2}
      whileDrag={{ scale: 1.1, cursor: "grabbing" }}
    >
      Drag me around
    </motion.div>
  );
}

Advanced Techniques

Layout Animations

Automatically animate layout changes:

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

export default function ExpandableCard() {
  const [isExpanded, setIsExpanded] = useState(false);

  return (
    <motion.div
      layout
      onClick={() => setIsExpanded(!isExpanded)}
      className="card"
    >
      <motion.h2 layout>Title</motion.h2>

      <AnimatePresence>
        {isExpanded && (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
          >
            Additional content
          </motion.div>
        )}
      </AnimatePresence>
    </motion.div>
  );
}

Path Animations (SVG)

Animate SVG paths for dynamic graphics:

export default function AnimatedLogo() {
  return (
    <svg width="200" height="200">
      <motion.path
        d="M10 10 L190 190"
        stroke="#C3F00F"
        strokeWidth="4"
        fill="transparent"
        initial={{ pathLength: 0 }}
        animate={{ pathLength: 1 }}
        transition={{ duration: 2, ease: "easeInOut" }}
      />
    </svg>
  );
}

Custom Spring Animations

export default function BouncyButton() {
  return (
    <motion.button
      whileHover={{ scale: 1.1 }}
      transition={{
        type: "spring",
        stiffness: 400,
        damping: 10,
      }}
    >
      Bouncy!
    </motion.button>
  );
}

Performance Best Practices

1. Use Transform Properties

Animate transform and opacity for best performance:

// Good - GPU accelerated
<motion.div animate={{ x: 100, scale: 1.2, opacity: 0.5 }} />

// Avoid - triggers layout recalculation
<motion.div animate={{ width: 200, marginLeft: 50 }} />

2. Optimize with layoutId

For smooth shared element transitions:

<motion.div layoutId="unique-id">Shared element</motion.div>

3. Reduce Motion for Accessibility

Respect user preferences:

import { useReducedMotion } from "framer-motion";

export default function AccessibleAnimation() {
  const shouldReduceMotion = useReducedMotion();

  return (
    <motion.div
      initial={{ opacity: 0, y: shouldReduceMotion ? 0 : 20 }}
      animate={{ opacity: 1, y: 0 }}
    >
      Content
    </motion.div>
  );
}

Real-World Example: Animated Navigation

export default function AnimatedNav() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <motion.button
        onClick={() => setIsOpen(!isOpen)}
        animate={{ rotate: isOpen ? 90 : 0 }}
      >
        Menu
      </motion.button>

      <AnimatePresence>
        {isOpen && (
          <motion.nav
            initial={{ x: "100%" }}
            animate={{ x: 0 }}
            exit={{ x: "100%" }}
            transition={{ type: "spring", damping: 20 }}
          >
            <motion.ul
              variants={{
                open: {
                  transition: { staggerChildren: 0.07, delayChildren: 0.2 },
                },
                closed: {
                  transition: { staggerChildren: 0.05, staggerDirection: -1 },
                },
              }}
              initial="closed"
              animate="open"
              exit="closed"
            >
              {menuItems.map((item, i) => (
                <motion.li
                  key={i}
                  variants={{
                    open: { y: 0, opacity: 1 },
                    closed: { y: 50, opacity: 0 },
                  }}
                >
                  {item}
                </motion.li>
              ))}
            </motion.ul>
          </motion.nav>
        )}
      </AnimatePresence>
    </>
  );
}

Common Pitfalls

1. Over-Animation

Don't animate everything. Be selective:

  • ✅ Important state changes
  • ✅ User interactions
  • ✅ Page transitions
  • ❌ Every hover effect
  • ❌ Continuous background animations

2. Ignoring Performance

Monitor frame rates:

import { useAnimationFrame } from "framer-motion";

useAnimationFrame((time, delta) => {
  if (delta > 16.67) {
    console.warn("Frame drop detected");
  }
});

3. Forgetting Exit Animations

Always wrap with AnimatePresence for exit animations:

<AnimatePresence>
  {isVisible && (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      Content
    </motion.div>
  )}
</AnimatePresence>

Results at Star Works

Implementing Framer Motion animations:

  • 65% increase in user engagement
  • 40% longer average session duration
  • 50% more interactions with CTAs
  • 95% positive user feedback on experience

Conclusion

Framer Motion empowers developers to create professional, performant animations with minimal code. By following best practices and using appropriate animation patterns, you can significantly enhance user experience.

Ready to bring your React application to life with stunning animations? Let's create something amazing together.


About the Author: Neha Verma is a Frontend Developer at Star Works specializing in React, animations, and interactive UI design. She has created award-winning interfaces for startups and enterprises.

Share this article

N

About Neha Verma

Frontend Developer at Star Works. Passionate about creating exceptional digital experiences.

View Full Profile →

Related Articles

Ready to Start Your Project?

Let's build something amazing together. Get in touch with our team to discuss your web development needs.

Get In Touch