Gesture & drag
Teaching your UI to understand flicks, swipes, and drags
Drag requires frame-by-frame response: zero perceptible lag, resistance at boundaries, natural settle on release.
Pointer capture
Without setPointerCapture, the element loses track of the cursor the moment it escapes bounds.
Always use pointer events, not mouse events. Pointer events unify mouse, touch, and stylus.
Momentum
Track the last 2-3 pointer positions with timestamps. Compute velocity as deltaPosition / deltaTime. Pass that velocity to a spring.
Motion's drag prop handles this automatically.
Boundary damping
Apply logarithmic damping past the boundary:
0.55 is iOS's value. 0.3 feels stiffer. 0.8 feels loose.
Swipe-to-dismiss
Use both distance and velocity, never just one.
If neither is met, spring back. On dismiss, animate off-screen with current velocity.
Touch-action and scroll conflict
Axis locking
On the first pointermove, lock to whichever axis has the larger delta.
Apply a 5-8px dead zone before checking.
The 60fps requirement
- Only animate
transformandopacity. - Use
will-change: transformon the dragged element. Remove it when drag ends. - Avoid React re-renders during drag. Use
useMotionValueor refs, not state.