Motion Reference

Motion is a production-ready animation library for React. Use it for fluid animations, gestures, and layout transitions.

Import

import { Plus, X } from "lucide-react";

interface Item {
  id: string;
  text: string;
}

const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: { staggerChildren: 0.1 },
  },
};

const itemVariants = {
  hidden: { opacity: 0, scale: 0.8 },
  visible: { opacity: 1, scale: 1 },
  exit: { opacity: 0, scale: 0.8, transition: { duration: 0.2 } },
};

export default function App() {
  const [items, setItems] = React.useState<Item[]>([]);

  const addItem = () => {
    setItems(prev => [...prev, { id: crypto.randomUUID(), text: `Item ${prev.length + 1}` }]);
  };

  const removeItem = (id: string) => {
    setItems(prev => prev.filter(item => item.id !== id));
  };

  return (
    <div>
      <motion.div
        whileHover={ { scale: 1.02 }}
        whileTap={ { scale: 0.98 }}
      >
        <button onClick={addItem} className="w-full">
          <Plus className="w-4 h-4" />
          Add item
        </button>
      </motion.div>

      <motion.div
        variants={containerVariants}
        initial="hidden"
        animate="visible"
        className="space-y-2"
      >
        <AnimatePresence mode="popLayout">
          {items.map(item => (
            <motion.div
              key={item.id}
              variants={itemVariants}
              exit="exit"
              layout
            >
              <div className="p-4 flex justify-between items-center">
                <span>{item.text}</span>
                <motion.button
                  whileHover={ { scale: 1.1 }}
                  whileTap={ { scale: 0.9 }}
                  onClick={() => removeItem(item.id)}
                  className="text-muted-foreground hover:text-destructive"
                >
                  <X className="w-4 h-4" />
                </motion.button>
              </div>
            </motion.div>
          ))}
        </AnimatePresence>
      </motion.div>
    </div>
  );
}

AnimatePresence Modes

// "sync" (default) - All enter/exit animations happen simultaneously
<AnimatePresence mode="sync">

// "wait" - Wait for exit animation to complete before entering
<AnimatePresence mode="wait">

// "popLayout" - Exiting items are removed from layout flow immediately
<AnimatePresence mode="popLayout">

Best Practices

  1. Use spring animations for natural-feeling motion on physical properties
  2. Use AnimatePresence for exit animations
  3. Use layout prop for smooth layout transitions
  4. Use variants for coordinated animations
  5. Keep animations subtle - 0.2-0.5s duration for most UI animations
  6. Use whileHover/whileTap for interactive feedback