Animations cheatsheet. Library used: Motion (formerly Framer Motion).
Install
npm i motion
import { motion } from "motion/react";
Basic
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
>
Hello
</motion.div>
Hover / tap
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Click
</motion.button>
Variants
const variants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
<motion.div initial="hidden" animate="visible" variants={variants} />
Variants are reusable + can be propagated to children:
const container = {
visible: { transition: { staggerChildren: 0.1 } },
};
<motion.ul initial="hidden" animate="visible" variants={container}>
{items.map((it) => (
<motion.li key={it.id} variants={item}>{it.name}</motion.li>
))}
</motion.ul>
AnimatePresence (exit anim)
import { AnimatePresence } from "motion/react";
<AnimatePresence>
{open && (
<motion.div
key="dialog"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
AnimatePresence watches direct children — they need stable keys. Without it, exit animations don’t run.
Layout animations
<motion.div layout>
...
</motion.div>
When the layout changes (size/position), motion animates to the new position. Magic for flexbox/grid reordering.
{items.map((it) => (
<motion.div key={it.id} layout>
{it.name}
</motion.div>
))}
LayoutId (shared element transitions)
{!expanded && <motion.div layoutId="card" />}
{expanded && <motion.div layoutId="card" className="fullscreen" />}
Motion animates between the two states across mount/unmount.
Spring transitions
<motion.div transition={{ type: "spring", stiffness: 200, damping: 20 }} />
Spring physics. Tune stiffness (higher = faster) and damping (higher = less bounce).
Stagger
const container = {
visible: {
transition: { staggerChildren: 0.05, delayChildren: 0.2 },
},
};
drag
<motion.div
drag
dragConstraints={{ left: 0, right: 300, top: 0, bottom: 200 }}
dragElastic={0.2}
/>
Track drag position:
<motion.div
drag="x"
onDragEnd={(e, info) => console.log(info.offset.x, info.velocity.x)}
/>
useMotionValue / useTransform
const x = useMotionValue(0);
const opacity = useTransform(x, [-100, 0, 100], [0, 1, 0]);
<motion.div style={{ x, opacity }} drag="x" />
Lower-level: subscribe to motion values without re-rendering.
Scroll-linked
import { useScroll, useTransform } from "motion/react";
const { scrollYProgress } = useScroll();
const opacity = useTransform(scrollYProgress, [0, 1], [1, 0]);
<motion.div style={{ opacity }} />
Whilst in view
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "0px 0px -100px 0px" }}
/>
Triggers when scrolled into view.
Animate gesture
<motion.button
animate={{ rotate: clicked ? 360 : 0 }}
transition={{ duration: 0.5 }}
onClick={() => setClicked(!clicked)}
/>
Reduce motion
import { useReducedMotion } from "motion/react";
const reduce = useReducedMotion();
<motion.div animate={reduce ? {} : { x: 100 }} />
Respect prefers-reduced-motion.
CSS transitions (sometimes better)
.card { transition: transform 200ms ease; }
.card:hover { transform: translateY(-4px); }
For simple state-driven animations, CSS is lighter than Motion.
View Transitions API (browser)
function nav(to: string) {
if (!document.startViewTransition) { setRoute(to); return; }
document.startViewTransition(() => { setRoute(to); });
}
Native cross-fades, no JS animation engine. Great for route transitions.
Common mistakes
- AnimatePresence with non-direct children — exit doesn’t run.
- Missing
keyon AnimatePresence children — same element, no exit. - Animating
width: auto— won’t animate. Animatewidth: 100%from0. - Heavy animations on layout-thrashing props — use
transform/opacity. - Forgetting
useReducedMotion— accessibility issue.
Read this next
If you want my animation snippets + reduced-motion utils, they’re at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .