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

Contents
- What does Motion solve in React?
- How do I animate layout without repainting the page?
- How do I build shared elements with layoutId and AnimatePresence?
- Motion or View Transitions API, which do I pick?
- How do I generate micro-interactions with Claude Code?
- What does the Waabi case teach?
- How do I hold fluidity and honor reduced motion?
- Frequently asked questions
- Conclusion: gesture, layout, and exits before the next component
- 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?
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.
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?
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.
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?
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.
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.
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?
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 effect | Owning prop or component | When to use it |
|---|---|---|
| Hover with tactile answer | whileHover with spring | Confirming focus on buttons and cards without blocking clicks |
| Press and release | whileTap with spring | Instant feedback on small controls and icons |
| List reorder | layout with spring | Animating filters, search, and grids without DOM remounts |
| Grid image | layout="position" | Holding image proportions through resizes and filters |
| Scroll entrances | whileInView and useScroll | Revealing a section once, with once true |
| Swap with a shared element | layoutId with AnimatePresence | Shared elements between thumb and detail with conducted exits |
| Curved path | arc() | Drawing the shared curve without slicing text or grids |
| Panel with inner scroll | LayoutGroup, layoutScroll, layoutRoot | Isolating 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.
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
Read next
Motion •
Motion Design for the Web: The Complete Guide
Scroll, text, images and video: the complete catalog of motion techniques for the web, with implementation in Next.js and the cases where each one pays off.
- motion
- scroll
The definitive guide — a Next.js site built around motion and scroll
The scroll foundation that, when missing, keeps the animations from working at all: Lenis, GSAP and Next.js wired in the right order and the mistakes to avoid.
- next.js
- lenis
Infra •
Documentation: deploying a Next.js application with GitHub + Hostinger
Every push becomes a live site with no hosting panel involved: connecting GitHub to Hostinger, the build settings that break and the checks after each deploy.
- deploy
- github


