The Loading Spinner That Wouldn’t Spin in San José

It was 11 p.m. in a Costa Rican co‑working loft. Daniela, a junior dev tuning in from Warsaw, DM’d me: “The spinner stalls the whole React page.” She’d wired a vanilla CSS keyframe that clashed with our Tailwind purge config. Ten minutes of frantic pair‑debugging later, we swapped the spinner to Framer Motion—one line of props, buttery smooth. The next day our design lead asked how the same trick would look in React Spring. That conversation morphed into this guide: a practical, side‑by‑side comparison of the two animation heavyweights powering most modern React apps.


Why Motion Libraries Still Matter

Attention spans keep shrinking; micro‑interactions guide users and surface state changes. React 19’s server components defer hydration, so client‑side animation must feel instantaneous. Framer Motion 12.23.6 introduces automatic reduced‑motion fallbacks and scroll‑linked timelines Yarn, while React Spring 10.0.1 refactored its physics core for 30 % smaller bundles npm. Knowing when to reach for each library saves kilobytes, dev hours, and remote debug sessions.


Quick‑Reference Toolbelt

Tool / ConceptOne‑liner purpose
Framer Motion 12Declarative prop‑based animation with variants & layout.
React Spring 10Physics‑driven hooks for fluid, interruptible motion.
Motion OneTiny Web‑Animations‑API wrapper (2 kB) for small widgets.
Lottie ReactJSON‑driven vector animations exported from After Effects.
CLI CommandWhat it does
npm i framer-motionInstalls Motion 12.23.x.
npm i @react-spring/webInstalls React Spring 10 core.
npm i motion oneAlternative micro‑library.
npm i eslint-plugin-jsx-a11yLints for reduced‑motion, tab order, etc.

Concept Primer — Plain English


Step‑by‑Step Walkthroughs

1 — Fade & Slide In: Framer Motion

tsxCopyEditimport { motion } from 'framer-motion';

export const Card = ({ children }: { children: React.ReactNode }) => (
  <motion.article
    initial={{ opacity: 0, y: 40 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ duration: 0.4, ease: 'easeOut' }}
    className="p-4 rounded bg-white shadow"
  >
    {children}
  </motion.article>
);

Line‑by‑line

2 — Physics Bounce: React Spring

tsxCopyEditimport { useSpring, animated } from '@react-spring/web';

export const BouncyCard = ({ children }: { children: React.ReactNode }) => {
  const styles = useSpring({
    from: { opacity: 0, y: 40 },
    to: { opacity: 1, y: 0 },
    config: { tension: 170, friction: 20 },
  });

  return (
    <animated.article style={styles} className="p-4 rounded bg-white shadow">
      {children}
    </animated.article>
  );
};

Highlights

3 — Hover & Tap Variants (Motion)

tsxCopyEdit<motion.button
  whileHover="hover"
  whileTap="tap"
  variants={{
    initial: { scale: 1 },
    hover: { scale: 1.05 },
    tap: { scale: 0.95 },
  }}
/>

No state hooks; just declarative variant names.

4 — Drag with Momentum (Spring)

tsxCopyEditimport { useSpring, animated } from '@react-spring/web';
import { useDrag } from '@use-gesture/react';

export const Draggable = () => {
  const [{ x, y }, api] = useSpring(() => ({ x: 0, y: 0 }));
  const bind = useDrag(({ offset: [ox, oy] }) => api.start({ x: ox, y: oy }));

  return <animated.div {...bind()} style={{ x, y }} className="w-32 h-32 bg-blue-500 rounded" />;
};

Gesture + spring delivers native‑feeling throw & settle.


Feature Matrix

FeatureFramer Motion 12React Spring 10
Bundle (gzip)~55 kB~20 kB
Variants / State✔ Built‑in❌ Use multiple springs
Shared LayoutlayoutId🚧 Manual measure + springs
SVG MorphpathLengthuseTrail over path points
3D / CanvasVia Framer Motion 3DVia @react-spring/three
GestureBasic drag; inertial drag pluginDeep via @use-gesture
Reduced Motion Auto‑respect✔ Prefers‑reduced‑motion CSS fallback☐ manual check

Common Pitfalls & Fixes

PitfallSymptomFix
Re‑created springs on every renderPerf stutterWrap configs in useMemo; store Motion variants outside component.
Hard‑coded durationsPrefers‑reduced‑motion users get nauseaGate with window.matchMedia('(prefers-reduced-motion)').
Layout shift on exitComponent unmounts instantlyMotion’s AnimatePresence or Spring’s useTransition preserve node until animation completes.
Oversized bundleFramer + Spring both installedStandardize on one; tree‑shake unused features with import { motion } from 'framer-motion/dom'.

Remote‑Work Insight Box

Our cross‑time‑zone rule: every animation PR must include a 10 second Loom video. Storybook’s Interaction tab can record these automatically. Colleagues in Panama comment while I’m asleep in Brazil, so by dawn the spring tension is dialed without a single synchronous meeting.


Performance & A11y Checkpoints


Optional Diagram Description

SVG: Two lanes—Declarative (Motion) and Physics (Spring). Each shows data flow: Props → Internal Scheduler → DOM vs. Hooks → Spring Engine → DOM. Arrows highlight where gestures feed Spring and where variants feed Motion.


Wrap‑Up — Choosing Your Animation Tool

Mastering these libraries turns static UIs into delightful experiences—without late‑night color‑contrast disasters. Which library will power your next project? Share your thoughts below; I’ll reply after my next remote stand‑up—maybe from a café in the Dominican Republic.

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x