From awesome-frontend
Best practices for animations in React that stay buttery-smooth (60fps, no jank) — because motion and performance have to coexist: a beautiful animation that stutters is worse than none. Use this whenever you add, review, or fix animation in a React/Next app: framer-motion / `motion` / `m`, springs, variants, `AnimatePresence`, layout animations, scroll reveals, parallax, gesture/hover micro-interactions, page or route transitions, staggered or kinetic text, count-ups, custom cursors, marquees, View Transitions. Trigger it even when the user just says "add some animation", "make it animated / interactive / alive", "animate this", "add a transition", or complains that an animation feels janky / laggy / stuttery / "travado". Core promise: keep React out of the per-frame loop and animate on the GPU compositor — every effect the designer wanted, paid for in the right place.
How this skill is triggered — by the user, by Claude, or both
Slash command
/awesome-frontend:awesome-react-animationsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Animation is where an interface earns the word "quality." But the same motion
Animation is where an interface earns the word "quality." But the same motion that delights at 60fps feels broken at 30. So in React, animation and performance are not two topics — they're one. Almost every janky React animation traces to the same root cause: React is doing work on a frame it shouldn't be on. Get React out of the per-frame loop and push the motion onto the GPU compositor, and smoothness stops being something you tune after the fact — it's structural.
Per frame the browser does: JS → Style → Layout → Paint → Composite. An
animation is cheap only if it stays at the bottom. Two properties animate on the
compositor alone, off the main thread: transform and opacity. Everything
else (width, top, box-shadow, filter, background, color) risks layout or paint
every frame. And in React there's a layer above all of this: a component that
calls setState 60×/sec re-runs render + reconciliation before the browser even
starts that pipeline.
So there are two golden questions for any animation:
transform/opacity? If not, find a way to make it so.1. Per-frame values never live in useState. Cursor position, tilt angle,
scroll progress, a spotlight's coordinates, a counting number — these change
every frame. Storing them in state means a render every frame. Use a motion
value (useMotionValue) which updates the DOM via the compositor without
re-rendering, or write straight to the node through a ref. This single rule
prevents most React animation jank. (See references/animation-performance.md.)
2. Animate transform and opacity; fake the rest. Don't animate a blur's
radius — pre-render the blur and animate its scale with will-change:transform
so the GPU reuses one cached texture. Don't animate width/height — use
scale. Don't animate top/left — use x/y (translate).
3. Reach for the library's primitives, not your own rAF + state.
framer-motion's useSpring, useTransform, useScroll, animate(), and the
whileHover/whileTap/drag props all run their loops outside React render.
Hand-rolling requestAnimationFrame + setState is the thing that's slow. (See
references/framer-motion-patterns.md.)
4. Ship less animation JS: LazyMotion + m. Use <LazyMotion features=…>
once at the root and the lightweight m component everywhere (m.div, not
motion.div) to defer ~30kb until needed. Big bundles delay hydration, which
is startup jank.
5. Promote continuously-animated elements deliberately.
will-change: transform (or translateZ(0)) gives a forever-looping element its
own compositor layer so it doesn't drag neighbors into repaints. Use it on the
few things that actually animate non-stop — not everywhere; each layer costs GPU
memory.
6. Respect prefers-reduced-motion. Gate continuous/large motion and
disable smooth-scroll for users who asked for less. It's accessibility and a
free perf win on weak hardware.
transform/opacity.m) — anything stateful or orchestrated: enter/exit of
lists (AnimatePresence), shared-layout transitions (layout), springs,
gesture-driven motion, scroll-linked parallax, staggered reveals. The default
for "real" UI animation in React.<ViewTransition>, startViewTransition) — route
and large UI-state changes where you want the browser to crossfade/morph
between two DOM states. Great for page transitions and list reorders without
hand-animating each element.Match the tool to the job; don't pull in framer-motion to fade one button CSS could handle, and don't hand-animate a route change View Transitions would do for free.
Animated components are Client Components. That's fine — but don't let one
animated widget turn a whole page into 'use client' and re-hydrate everything.
Keep pages as Server Components and isolate motion in small client islands:
one reusable <FadeIn> reveal wrapper, one <Carousel>, one <ContactChat>.
Less client JS = faster hydration = the first interaction doesn't stutter. (See
references/scroll-and-reveal.md for the reveal-island pattern.)
m → View
Transitions).transform/opacity; if you're tempted to animate something
else, find the transform equivalent.backdrop-filter/mix-blend-mode/filters, so an FPS
reading there can look perfect while real hardware chugs. Record a DevTools
Performance trace over the interaction, or say plainly the user should feel it
on their machine.references/animation-performance.md — the jank catalog: per-frame setState,
animated blur/backdrop-filter/mix-blend-mode, layout-triggering properties,
passive listeners, measuring honestly. Before/after for each.references/framer-motion-patterns.md — motion values vs state, springs,
useTransform, AnimatePresence, layout animations, variants + stagger,
gestures, LazyMotion/m, imperative animate().references/scroll-and-reveal.md — whileInView/useInView reveals, scroll-
linked parallax with useScroll/useTransform, the server-page + reveal-island
pattern, smooth-scroll tradeoffs, content-visibility interplay.In React, "make it smooth" and "make it animated" are the same instruction: keep React off the per-frame path, animate on the compositor, and the motion is fast because of how it's built — not because you optimized it later.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub gcomartins/myfskills --plugin awesome-frontend