Natural Gestures in React Native Start With Physics

Draggable UI in React Native is easy to get working but hard to make feel right. The difference usually comes down to a handful of details: whether a drag decision accounts for velocity, how the settle animation is timed, and whether the element tracks the finger with the right amount of resistance. The Sheet component in Shopify's Shop app is a good case study for these patterns.

The sheet can be dragged up or down by the user. When released, it animates back to its open position or slides off the bottom of the screen to close. A first implementation uses a gesture handler that assigns the finger's Y position directly to the component state, then decides on release whether to animate open or closed based on where the finger stopped.

Read the Velocity, Not Just the Position

Deciding the final state purely on finger position creates a frustrating interaction. If someone flicks the sheet downward quickly from the top, the gesture ends near the top even though the intent was clearly to close it. The result is a sheet that snaps back open despite a decisive swipe.

The fix is to include event.velocityY from the onEnd callback in the decision. Adding the velocity to the final position—with a multiplier to tune how much influence momentum should have—makes the close action match user intent much more closely. A fast downward flick now closes the sheet; a deliberate slow drag can still hold it open.

Springs Fix the Settle Animation

A fixed-duration animation has a second problem: the travel time to the open or closed position is always the same, regardless of distance. A sheet 50 pixels from its destination takes as long as one 500 pixels away, and the speed of the finger at release has no effect on how quickly the sheet settles.

Replacing the timed animation with a spring animation solves both issues. withSpring from React Native Reanimated factors in both the remaining distance and the release velocity, producing a physically plausible motion. The sheet moves with the momentum of the drag and eases into place over a natural duration. The result is a component that can be flicked quickly into either state and animates there in a way that feels consistent with how it was thrown.

Add Resistance Where There Is No Travel

Another subtle cue comes from how the element responds to the finger when there is nowhere to go. If the open sheet is dragged upward and it tracks the finger one-to-one, the gesture feels lifeless—and worse, it invites the user to keep pulling with no functional outcome.

A better approach is to make the element move slower than the finger when dragging against a boundary. Dividing the dragged distance so the element moves only a fraction of the finger's travel introduces a rubber-band feel. The user senses resistance and learns intuitively that the sheet cannot be pulled further open. The same principle applies to any draggable content that must communicate its limit: photo viewers, story viewers, and pull-to-close patterns all benefit from introducing this elasticity at the edges.

These refinements—velocity-aware end states, spring-based settling, and boundary resistance—turn a mechanically correct gesture into something that feels responsive and tactile. Getting the physics right on the small details compounds into an overall touch experience that users can feel.