Skip to content
Zumkai

Micro-interactions with Motion in Claude Code: layout, gestures, and AnimatePresence (with the Waabi case)

Micro-interactions with Motion in Claude Code: transform layout, spring gestures, shared element with layoutId and AnimatePresence, prompts, and the Waabi case.

  • motion framer micro interactions claude code
  • motion layout animatepresence
Dark navy cover with the title Micro-interactions with Motion and Claude Code in condensed type, a violet-to-pink gradient rule, and concentric rings on the right.
Contents
  1. What does Motion solve in React?
  2. How do I animate layout without repainting the page?
  3. How do I build shared elements with layoutId and AnimatePresence?
  4. Motion or View Transitions API, which do I pick?
  5. How do I generate micro-interactions with Claude Code?
  6. What does the Waabi case teach?
  7. How do I hold fluidity and honor reduced motion?
  8. Frequently asked questions
  9. Conclusion: gesture, layout, and exits before the next component
  10. Sources

Micro-interactions fail when three logics tangle with no owner. Gestures want instant response, lists must hold position through filters, scene exits must explain where the element went. Once everything turns into generic fixed-duration animation, clicks feel dead and lists blink on every search.

This tutorial splits those roles with Motion in React. You set up motion/react, apply spring gestures, animate layout with transform, build shared elements with layoutId and AnimatePresence, settle the View Transitions API call on technical merit, and generate the set with Claude Code from structured prompts. For the full library map, return to the complete motion guide.

What does Motion solve in React?

React code with a Motion component in a dark editor
Photo: Negative Space via StockSnap (CC0).

Motion solves what belongs to the component cycle. Layout shifting under filters, items leaving lists, buttons answering hover and press, cards expanding into detail. Those cases want interruptible physics plus declared exits, not scroll timelines.

The current setting helps place the pick. Generation v13 marks the post-Framer Motion phase, importing from motion/react and averaging 42M npm downloads a week, per motion.dev and npm. Core features like layout, AnimatePresence, and animateView run free, plus Motion UI launched Jul 2026, per the same docs. The ecosystem adds a search MCP named search-motion-codex and a Cursor partnership, with packages published on npm.

The prop set covers this whole tutorial. Use layout for position and size changes, layoutId for a shared element across two places, AnimatePresence for exits, gestures like whileHover, whileTap, and whileInView for response and entrances, springs with stiffness and damping for physics, plus useScroll for scroll ties inside components. The block below shows the minimal gesture-with-spring pattern.

jsx
import { motion } from "motion/react";

export function ActionButton() {
  return (
    <motion.button
      whileHover={{ scale: 1.04 }}
      whileTap={{ scale: 0.97 }}
      transition={{ type: "spring", stiffness: 400, damping: 25 }}
    >
      See case
    </motion.button>
  );
}

That short block carries three decisions. Hover confirms focus without stealing the click, tap confirms press with a small shrink, and springs allow mid-gesture interrupts with no jump. Fixed durations with linear easing never deliver that behavior, because they ignore fast direction changes.

How do I animate layout without repainting the page?

Laptop with a grid interface during a layout animation
Photo: Negative Space via StockSnap (CC0).

Layout animates well through transform instead of box recalculation. Motion converts layout changes into transform, which skips fresh paint and holds lists steady through filters, searches, and reorders. Without that conversion, every size change triggers layout math and long lists lose fluidity.

The layout prop switches that behavior on the element itself. For grid images, prefer layout="position", which holds proportions and prevents distortion mid-resize, per Motion's layout docs. For nested settings, use LayoutGroup to group related transitions, layoutScroll for panels with inner scroll, and layoutRoot to isolate measuring when a modal or drawer lives outside the main flow. Short springs with stiffness between 260 and 400 and damping between 25 and 32 stay responsive without excess bounce.

jsx
import { motion } from "motion/react";

export function Grid({ items }) {
  return (
    <div className="grid">
      {items.map((item) => (
        <motion.div
          key={item.id}
          layout
          transition={{ type: "spring", stiffness: 300, damping: 30 }}
        >
          {item.title}
        </motion.div>
      ))}
    </div>
  );
}

For images, the direct variation is <motion.img layout="position" />, which anchors position without forcing content scale. The common mistake here is animating width and height in CSS while asking for springs on top, which creates two sources of truth for one size. Leave measuring to Motion and keep CSS for grid and spacing.

a three-check layout rule (the state belongs to the component, the change preserves identity with a stable key, no route swap) separates layout from layoutId before the prompt and avoids shared elements where plain layout sufficed.

How do I build shared elements with layoutId and AnimatePresence?

Screen with component code during a shared-element transition
Photo: Marc Chouinard via StockSnap (CC0).

A shared element is one layoutId in two places. Thumbnail and detail declare the same identifier, and Motion moves a single visual element between both states instead of unmounting one and mounting another. Without that bond, the transition degrades into a generic fade and readers lose where the content came from.

AnimatePresence conducts the exit. It holds the old element on stage until the exit animation finishes, which lets exit and entrance overlap with no hole. The popLayout mode helps when the exit must never push the remaining layout, because it pulls the element out of flow mid-animation. For paths crossing text or grids, the arc() function draws a readable curve instead of a straight line cutting content, per Motion's docs.

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

export function Card({ open, onClose }) {
  return (
    <AnimatePresence mode="popLayout">
      {open && (
        <motion.div
          layoutId="waabi-card"
          transition={{ type: "spring", stiffness: 260, damping: 28 }}
          onClick={onClose}
        />
      )}
    </AnimatePresence>
  );
}

That pattern demands identity discipline. The layoutId stays unique per origin-destination pair, and the list key stays stable through filters. In long lists with dozens of animated cards, share only visible items and use whileInView with once for the rest. Excess sharing breeds measurement fights and sinks mobile reading.

Motion or View Transitions API, which do I pick?

The honest pick runs through snapshot versus transform. The View Transitions API captures a DOM snapshot and animates that still image across states, which simplifies document transitions with little code. Motion animates the living element with transform, which preserves hierarchy, events, and mid-gesture interrupts.

That split settles three cases. Interrupts favor Motion, because a fresh gesture can redirect the spring without waiting out the transition. Pointer events favor Motion on controls that must stay clickable mid-animation, because snapshots freeze the image and block interaction until done. Orchestration cost favors View Transitions when the change spans a whole page with no competing gesture, because it demands less per-component exit code. Motion's animateView feature, free per motion.dev, serves whoever wants view transitions without leaving the component model.

In practice, use View Transitions for document swaps where a snapshot suffices with no competing physics. Use Motion for component micro-interactions with hover, drag, filters, and shared elements, where interrupts and live events decide the feel. Mixing both in one transition double-animates the same pixels and deserves avoiding.

How do I generate micro-interactions with Claude Code?

Good generation splits three roles. Skills set the standard, prompts set the structure, docs verify the API. The start is the iart-ai/web-animation-skills repo on GitHub, with skills including micro-interaction, 60fps-animation, and accessible-animation, per the iart-ai repo. The micro-interaction skill teaches gestures, springs, and exits, the 60fps skill teaches transform and opacity, the accessible skill teaches reduced-motion preference.

Prompts work better with explicit structure. Describe the component, animated props, layoutId wherever sharing happens, exit behavior, and mobile rules. One direct example for a shared card: "Build a card that expands to a detail view with layoutId and AnimatePresence using Motion." Then add component names, the AnimatePresence mode, arc() curves wherever crossings happen, and where LazyMotion enters to trim the bundle.

For a lean bundle, use the jezweb Motion skill. It documents LazyMotion at 4.6KB and useAnimate at 2.3KB for React 19 and Next 16, so pages load only the animation subset they use. For principles, use the Design Motion Principles material tied to Emil Kowalski, Krehel, and Jhey, which organizes duration, easing, and gesture response with reviewable examples. To verify APIs, use the search-motion-codex MCP and the Cursor partnership cited on motion.dev and npm. The toolkit tied to wilwaldon organizes the rest of the flow, with Playwright MCP plus Chrome DevTools MCP audits.

One boundary prevents stack mistakes. The gsap-react skill belongs to the GSAP flow with the useGSAP hook and stays out of this tutorial, because here the cycle belongs to Motion with layout and AnimatePresence. For scroll with pin, scrub, and parallax, go deep on cinematic scroll with GSAP and Lenis.

jsx
import { motion, useScroll } from "motion/react";

export function Reveal() {
  const { scrollYProgress } = useScroll();
  return (
    <>
      <motion.div style={{ scaleX: scrollYProgress }} className="progress" />
      <motion.section
        initial={{ opacity: 0, y: 24 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true, margin: "-80px" }}
      />
    </>
  );
}

That block shows viewport entrances with no scroll timeline. The viewport with once avoids re-animating every pass, and useScroll stays reserved for progress tied to bars or indicators, not gesture physics.

What does the Waabi case teach?

Desk with a laptop showing an interface with micro-interactions
Photo: Negative Space via StockSnap (CC0).

Waabi reads as proof of restraint. The site, SOTD by the Antinomy studio with React, Motion, and Next.js, pairs background canvas animation with interface microinteractions, per the Awwwards animation picks. Canvas does texture and depth while Motion does gesture response, section entrances, and state transitions. No effect fights product reading.

The table below compresses gesture and effect, owning prop, and when to reach for each. It works as triage before any prompt and stops you from ordering physics where a plain exit sufficed.

Gesture or effectOwning prop or componentWhen to use it
Hover with tactile answerwhileHover with springConfirming focus on buttons and cards without blocking clicks
Press and releasewhileTap with springInstant feedback on small controls and icons
List reorderlayout with springAnimating filters, search, and grids without DOM remounts
Grid imagelayout="position"Holding image proportions through resizes and filters
Scroll entranceswhileInView and useScrollRevealing a section once, with once true
Swap with a shared elementlayoutId with AnimatePresenceShared elements between thumb and detail with conducted exits
Curved patharc()Drawing the shared curve without slicing text or grids
Panel with inner scrollLayoutGroup, layoutScroll, layoutRootIsolating measuring in modals, drawers, or self-scrolling panels

The chart below places adoption before stack talk. Motion averages 42M npm downloads a week against GSAP's 4.6M in the same window, per npm, motion.dev, and gsap.com. The number never says which is better. It says where each community tests more code in production React.

Weekly npm downloads: Motion versus GSAP Horizontal bar chart with Motion at 42 million and GSAP at 4.6 million weekly npm downloads. Sources linked under Sources. Weekly npm downloads (in millions) Motion 42M 42 GSAP 4.6M 4.6 Read: Motion concentrates use in React components; GSAP concentrates use in narrative scroll and timelines. Source: npm via motion.dev and gsap.com, 2026
Source: weekly npm averages via motion.dev and gsap.com in 2026, links under Sources.

Waabi's repeating pattern is motive before physics. One idea per viewport, short-answered gestures, exits explaining changes, canvas backgrounds never fighting text. The React-with-Motion-and-Next.js stack serves that plan because it holds animation close to the state firing it.

How do I hold fluidity and honor reduced motion?

Fluidity follows a stable rule. Animate transform and opacity, hold springs short, reserve space for layout shifts, audit in the browser with Playwright MCP and Chrome DevTools MCP. Layout lists with no stable key remap items and jump, shared elements with no AnimatePresence cut exits, long gestures delay click confirmation.

Accessibility holds the same bar with MotionConfig. Use reducedMotion="user" to honor prefers-reduced-motion, with a legible static version and no lost content for reduced-motion users. Pair with whileInView and once to stop re-animating every scroll, and useScroll only where progress truly ties to scrolling. On mobile, cut simultaneous sharing and prefer fades with short shifts over long curves across text.

Rive vs Lottie with Claude Code

Frequently asked questions

Are layout and layoutId the same thing?

No. The layout prop animates position and size changes on the element itself, like a grid filter reordering without DOM remounts. layoutId connects two distinct elements standing for one content, like thumbnail and detail in a shared element. Use layout for staying put with new geometry and layoutId for continuity across two places, with AnimatePresence conducting the exit.

When does the View Transitions API suffice?

When the transition covers a document and the snapshot settles it with no competing gesture. With no hover, drag, or mid-flight interrupt, snapshots deliver clean swaps with little code. With interruptible gestures, pointer events mid-animation, or spring physics, prefer Motion with transform on the living element, per Motion's docs and the browser transitions API model.

Which prompt do I send Claude Code for a shared-element card?

Start with the direct example: "Build a card that expands to a detail view with layoutId and AnimatePresence using Motion." Then add structure: origin and destination component names, the layoutId value, popLayout mode, arc() curves wherever crossings happen, springs with stiffness and damping, mobile behavior. Verify the API with the search-motion-codex MCP before generating long orchestrations.

How do I shrink the Motion bundle in Next.js?

Use LazyMotion to load only the subset in use, at a documented 4.6KB, and useAnimate for short imperative animations, at a documented 2.3KB, for React 19 and Next 16, per the jezweb Motion skill. Load the provider once atop the tree and hold gestures plus layout in leaf components. Measure the bundle after swapping, because the win follows how many props stay on the page.

Conclusion: gesture, layout, and exits before the next component

Good micro-interactions want three hits in sequence. Spring gestures for instant, interruptible response, transform layout for filter work with no fresh paint, shared elements with layoutId and AnimatePresence for continuity across screens. The call against View Transitions runs through snapshot versus transform, watching interrupts and pointer events. Claude Code generation pays best with a micro-interaction skill, a structured prompt, and search-motion-codex verification.

The next step follows the project's bottleneck. For narrative scroll, pin, and parallax, return to the GSAP-with-Lenis satellite. For interactive vectors with states and light loops, advance to Rive versus Lottie.

Sources