MotionReactAnimationNext.js

Motion for React Guide: Animation, Gestures, and Exit Effects

Learn Motion for React with motion components, variants, gestures, drag, MotionValue, useTransform, AnimatePresence, Next.js client components, and performance boundaries.

·Updated ·13 min read·Counting...
Motion for React Guide: Animation, Gestures, and Exit Effects

Motion for React is useful when animation is tied to React state, layout changes, user gestures, or component exit states. Simple hover effects, color fades, and basic transitions should usually stay in CSS. Reach for Motion when you need variants, drag, MotionValue, useTransform, or AnimatePresence.

Install and import

npm install motion

Import from the current React package path:

import { motion } from "motion/react"

In Next.js App Router, interactive animation components usually need to be Client Components:

"use client"

import { motion } from "motion/react"

export function WelcomeCard() {
  return (
    <motion.article
      initial={{ opacity: 0, y: 16 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.35, ease: "easeOut" }}
    >
      Welcome to FreeMac
    </motion.article>
  )
}

Older projects may still use framer-motion. Avoid mixing import paths casually.

Basic motion states

<motion.button
  initial={{ scale: 0.96, opacity: 0 }}
  animate={{ scale: 1, opacity: 1 }}
  whileHover={{ scale: 1.03 }}
  whileTap={{ scale: 0.98 }}
  transition={{ type: "spring", stiffness: 320, damping: 24 }}
>
  Get started
</motion.button>

Common props:

  • initial: starting state
  • animate: target state
  • transition: timing, easing, and spring config
  • exit: leaving state, used with AnimatePresence
  • whileHover, whileTap, whileDrag: gesture states

Prefer x, y, scale, rotate, and opacity for many UI animations. Avoid repeatedly animating layout-heavy properties when a transform can express the same motion.

Variants

Variants are useful when parent and children share semantic animation states:

const list = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: { staggerChildren: 0.08 },
  },
}

const item = {
  hidden: { opacity: 0, y: 12 },
  visible: { opacity: 1, y: 0 },
}

export function ToolList({ tools }: { tools: string[] }) {
  return (
    <motion.ul variants={list} initial="hidden" animate="visible">
      {tools.map((tool) => (
        <motion.li key={tool} variants={item}>
          {tool}
        </motion.li>
      ))}
    </motion.ul>
  )
}

For one element, direct animate props are often easier to read. Use variants when orchestration makes the state names valuable.

Drag gestures

<motion.div
  drag="x"
  dragConstraints={{ left: -120, right: 120 }}
  dragElastic={0.15}
  whileDrag={{ scale: 1.04, cursor: "grabbing" }}
  style={{ cursor: "grab" }}
>
  Drag horizontally
</motion.div>

Drag elements need clear constraints. On mobile, test conflicts with scroll gestures. Do not make animation the only way to complete a core task.

MotionValue and useTransform

MotionValue can update animation values without causing React re-render on every frame. useTransform maps one value range to another:

"use client"

import { motion, useMotionValue, useTransform } from "motion/react"

export function SwipeCard() {
  const x = useMotionValue(0)
  const rotate = useTransform(x, [-200, 0, 200], [-12, 0, 12])
  const opacity = useTransform(x, [-200, 0, 200], [0.35, 1, 0.35])

  return (
    <motion.article
      drag="x"
      dragConstraints={{ left: 0, right: 0 }}
      style={{ x, rotate, opacity }}
    >
      Drag card
    </motion.article>
  )
}

Use this for continuous values and gesture mapping. For ordinary UI toggles, React state is simpler.

AnimatePresence

React normally removes conditionally rendered elements immediately. AnimatePresence lets elements play an exit animation first:

"use client"

import { AnimatePresence, motion } from "motion/react"

export function Modal({ open }: { open: boolean }) {
  return (
    <AnimatePresence>
      {open && (
        <motion.div
          key="modal"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
        >
          <motion.section
            initial={{ scale: 0.96, y: 16 }}
            animate={{ scale: 1, y: 0 }}
            exit={{ scale: 0.96, y: 12 }}
          >
            Modal content
          </motion.section>
        </motion.div>
      )}
    </AnimatePresence>
  )
}

Direct children need stable unique keys. For route-like transitions, check mode options and layout behavior carefully.

Performance and accessibility checklist

  • Prefer transform and opacity.
  • Avoid many invisible infinite animations.
  • Tune spring stiffness and damping instead of stacking transitions.
  • Use stable list keys.
  • Respect reduced-motion preferences.
  • Test drag, scroll, and page transitions on lower-performance devices.

Subscribe to FreeMac

Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.