
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 stateanimate: target statetransition: timing, easing, and spring configexit: leaving state, used withAnimatePresencewhileHover,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
transformandopacity. - Avoid many invisible infinite animations.
- Tune spring
stiffnessanddampinginstead of stacking transitions. - Use stable list keys.
- Respect reduced-motion preferences.
- Test drag, scroll, and page transitions on lower-performance devices.
Related FreeMac guides
- For React Hooks, read React Hooks Guide: State, Effects, Context, and Reducers.
- For rendering performance, read Reflow and Repaint: Why CSS Animations Get Janky.
- For 3D animation choices, read Three.js Animation: GSAP vs AnimationMixer.
Continue reading
React Hydration Failed: Causes, Debugging, and Fixes
Debug server and client rendering mismatches caused by dates, random values, browser APIs, invalid HTML, local storage, locale differences, and DOM-changing extensions.
11 min readNext.js Image Guide: sizes, fill, and Remote Images
Use the modern Next.js Image component with width and height, fill, sizes, preload, placeholders, remotePatterns, and clear rules for avoiding outdated layout props.
11 min readNext.js Routing Guide: App Router Patterns
Learn the current Next.js App Router model: static routes, dynamic segments, catch-all routes, layouts, route groups, parallel routes, and route handlers.
Subscribe to FreeMac
Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.