DEV Community

Atlas Whoff
Atlas Whoff

Posted on

Framer Motion: Animations That Don't Hurt Performance

Framer Motion: Animations That Don't Hurt Performance

CSS animations get you 80% of the way. Framer Motion handles the hard 20%: gesture animations, layout transitions, shared element transitions, and complex sequences.

Install

npm install framer-motion
Enter fullscreen mode Exit fullscreen mode

Basic Animation

import { motion } from 'framer-motion';

// Animate on mount
function Card() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.3, ease: 'easeOut' }}
      className='bg-white rounded-lg p-6'
    >
      Content
    </motion.div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Hover & Tap

function Button({ children }) {
  return (
    <motion.button
      whileHover={{ scale: 1.02 }}
      whileTap={{ scale: 0.98 }}
      transition={{ type: 'spring', stiffness: 400, damping: 25 }}
    >
      {children}
    </motion.button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Scroll-Triggered Animations

function Feature({ title, description }) {
  return (
    <motion.div
      initial={{ opacity: 0, y: 40 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true, margin: '-50px' }}
      transition={{ duration: 0.5 }}
    >
      <h3>{title}</h3>
      <p>{description}</p>
    </motion.div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Staggered Children

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

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

function ProductList({ products }) {
  return (
    <motion.ul variants={container} initial='hidden' animate='show'>
      {products.map(p => (
        <motion.li key={p.id} variants={item}>
          <ProductCard product={p} />
        </motion.li>
      ))}
    </motion.ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

AnimatePresence (Mount/Unmount)

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

function Modal({ isOpen, onClose, children }) {
  return (
    <AnimatePresence>
      {isOpen && (
        <motion.div
          initial={{ opacity: 0, scale: 0.95 }}
          animate={{ opacity: 1, scale: 1 }}
          exit={{ opacity: 0, scale: 0.95 }}
          transition={{ duration: 0.2 }}
          className='fixed inset-0 z-50'
        >
          {children}
        </motion.div>
      )}
    </AnimatePresence>
  );
}
Enter fullscreen mode Exit fullscreen mode

Layout Animations

// Animate layout changes automatically
function ExpandableCard() {
  const [expanded, setExpanded] = useState(false);
  return (
    <motion.div layout onClick={() => setExpanded(!expanded)}
      className='bg-white rounded-lg p-4 cursor-pointer'
    >
      <motion.h3 layout='position'>Card Title</motion.h3>
      {expanded && (
        <motion.p initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
          Expanded content here...
        </motion.p>
      )}
    </motion.div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Performance Tips

  • Animate transform and opacity only — GPU-accelerated
  • Avoid animating width, height, top, left — triggers layout reflow
  • Use will-change: transform for elements that animate frequently
  • viewport={{ once: true }} — don't re-animate on scroll back

Framer Motion animations ship throughout the landing page and dashboard in the AI SaaS Starter Kit. $99 at whoffagents.com.

Top comments (0)