framer-motion-gestures-drag
Motion for React: gestures, drag, and motion values
When to use this
- You need draggable elements (cards, sliders, bottom sheets, kanban items) with realistic constraints, elastic overscroll, and momentum/inertia on release.
- You need declarative gesture-reactive states (
whileHover,whileTap,whileFocus,whileInView) without manually wiringonMouseEnter/onMouseDownhandlers and state. - You need one continuously-updating value (drag position, scroll position, a slider) to drive OTHER visual properties reactively (rotation while dragging, parallax offset, a color shift), which is what
useMotionValue+useTransformchains are for. - You want an external control surface for drag (start a drag programmatically from a "handle" icon elsewhere in the component) via
useDragControls. - Do NOT use this for layout-position transitions triggered by state/prop changes with no direct user gesture; that is
framer-motion-layout. Do NOT use this for non-React vanilla JS gesture handling; there is no direct sibling skill for that here, drop to native pointer events ormotion-one-waapi.
Mental model
Motion values (useMotionValue) are the core primitive underneath nearly everything gesture-related in Motion for React, and understanding them is the key to not fighting the library. A MotionValue is a mutable container for a single animatable value that lives OUTSIDE React's render cycle: updating it (via .set(), or automatically through a drag gesture) does not trigger a React re-render, it directly writes to the DOM (typically as a transform sub-value or another style property) on the next animation frame. This is precisely why drag in Motion is smooth even in apps with expensive component trees: dragging a card does not re-render your React tree 60 times a second, it mutates motion values and lets Motion push those directly to style, sidestepping React's reconciler entirely for the high-frequency part of the interaction.
useTransform(motionValue, inputRange, outputRange) creates a NEW derived MotionValue that recomputes whenever the source value changes, mapping its range to a different output range (numeric, color, or an arbitrary transform function). This is how you build "linked" motion: dragging a card's x MotionValue can drive a derived rotate MotionValue (useTransform(x, [-200, 200], [-15, 15])) so the card tilts as it is dragged sideways, entirely without React state or re-renders, and entirely reactive to raw pointer movement.
The drag prop on a motion component wires up pointer event handling (pointerdown/move/up) to directly update that element's x/y motion values during the gesture, apply dragConstraints (a bounding box, either literal pixel values or a ref to a container element whose bounds are measured automatically) with dragElastic controlling how much overscroll beyond the constraint is allowed (0 = hard stop, 1 = no resistance, values between are a rubber-band feel), and on release, runs a momentum/inertia animation using the gesture's exit velocity (captured via useVelocity internally) so the element continues moving briefly and decelerates, rather than stopping dead the instant the pointer lifts. dragMomentum={false} disables this inertia phase for interactions that should stop exactly where released.
useDragControls decouples "what element is draggable" from "what element starts the drag gesture." Normally the drag-enabled element itself listens for the pointer-down that begins dragging; with useDragControls, you can call controls.start(event) from a completely different element (a dedicated drag handle icon), useful for list items where only a small handle region, not the whole row, should initiate a drag.
whileHover/whileTap/whileFocus/whileDrag are declarative variant objects: while the named gesture state is active, Motion animates the component TO that object's values, and automatically animates back to the base animate state when the gesture ends, without you managing any state or event handlers yourself. useSpring(motionValue, config) wraps a raw motion value in a spring simulation, so that instead of a value's derived outputs snapping instantly, they follow a physically damped approach to the target, commonly used to smooth a fast-changing raw input (mouse position, scroll) into a lagged, organic-feeling follower value.