Gestures

Gestures are a very important aspect of user interfaces, they are what makes the elements feel alive. The user performs an action and the interface responds appropriately.

Gestures in Motion

We interact with interfaces constantly, we hover over buttons, tap links and drag cards. Motion extends React’s basic event system with a simple set of gestures that make these interactions feel alive.

Hover
Tap
Pan
Drag
Focus
InView

To use gestures in Motion we have to use the motion component. It supports hover, tap, pan, drag, focus, and inView gestures.

whileHover
whileTap
onPan
whileDrag
whileFocus
whileInView

Each of these gestures can trigger animations through while- props or be handled manually with event listeners.

Hover

Hover is the most obvious gesture, but still worth mentioning. whileHover detects when a pointer enters or leaves the element.

The great part about Motion is that you can easily use spring animations for any of these gestures, which is pretty difficult in CSS.

<motion.div
  whileHover={{ rotate: 180 }}
  transition={{ type: "spring", duration: 0.8, bounce: 0 }}
/>

For simple effects like these, both CSS and Motion work well. I personally prefer Motion, because I am able to use springs, which create more natural looking motion.

None
CSS
Motion

Performance is worth keeping in mind, but for effects that involve transform, clipPath or filter CSS and Motion behave almost identically.


Even though Motion uses Javascript, these animations still run on the compositor thread, just like CSS. I covered this a bit in my will-change in CSS article.


In most cases, there’s no meaningful performance difference. Sometimes Motion's performance is even better, because it handles independent transforms more efficiently than CSS variables.

Tap

The whileTap prop fires when a pointer or a keyboard key presses and releases on the same element.

Tap automatically cancels if the pointer moves too far so it won’t conflict with drags.

<motion.div
  whileTap={{ scale: 0.8 }}
  transition={{ type: "spring", duration: 0.5, bounce: 0 }}
/>

It’s also keyboard accessible. Pressing Enter will trigger onTapStart and whileTap, releasing Enter fires onTap.

None
CSS
Motion

Drag

The drag gesture applies pointer movement directly to a component. Motion provides a lot of controls out of the box. To make a component draggable, you can simply add the drag prop to a motion component.

You can then add whileDrag and the property that you want to animate. In this example, I also added dragConstraints, so the element can't be dragged outside of its container.

<motion.div
  whileDrag={{ scale: 0.8 }}
  drag
  dragConstraints={constraintsRef}
  transition={{ type: "spring", duration: 0.5, bounce: 0 }}
/>

The drag gesture is extremely flexible, the Motion Docs cover many more use cases and configurations.

TC
Tim Cook9:41 AM
Liquid Glass
Apple introduces a delightful and elegant new software design.

Pan

The pan gesture recognizes when a pointer moves more than 3 pixels while pressed and ends when it’s released.

Pan doesn't have an associated while- prop, so you'll need to use event handlers like onPan, onPanStart and onPanEnd.

<motion.div
  onPanStart={() => setIsPanning(true)}
  onPanEnd={() => setIsPanning(false)}
  animate={{ scale: isPanning ? 0.9 : 1 }}
/>

Circling back to spring animations, a slider component like this is a place where it's definitely worth using spring animations in my opinion, as the motion here looks natural.

Volume
50%

Focus

The whileFocus prop fires when an element gains or loses focus. It follows the same logic as :focus-visible in CSS.

Use Tab to Focus

This adds subtle visual feedback for keyboard navigation or accessibility flows.

<motion.div
  tabIndex={0}
  whileFocus={{ scale: 1.1 }}
  transition={{ type: "spring", duration: 0.3, bounce: 0 }}
/>

It is great for elements like buttons, input fields and others that you want to have a clear visual response when they are focused.

Use Tab to Focus

Since you can animate almost any value, you can get very creative with implementing even more complex effects.

Use Tab to Focus

However, I'd recommend keeping focus animations subtle. Overly complex motion can feel distracting or even disorienting when navigating by keyboard.

In View

whileInView triggers when an element enters or exits the viewport.

Scroll Down

It is great for scroll-based reveals or progressive section entrances. You can also pair it with the initial prop to make the component animate from and to a specific state.

<motion.div
  initial={{ filter: "blur(8px)" }}
  whileInView={{ scale: 1.1, rotate: 45, filter: "blur(0px)" }}
  viewport={{ root: scrollRef, amount: 0.5 }}
  transition={{ type: "spring", duration: 0.6, bounce: 0 }}
/>

Like in this example above, where the initial (unrevealed) value of the blur filter is 8px and once the component is in view, it animates to 0px. There are a lot of great examples and creative ways to use this in the Motion Docs.

Conclusion

A lot of the things covered here can be found in the Motion Documentation.

This article is by no means supposed to just recite things that are written in the documentation, but instead point out that this something that is available to do with Motion, show how I use it in my work and showcase examples of where I use them.


Huge thanks to Matt Perry for providing feedback on this article.

More

In case you have any questions reach me at jakub@kbo.sk, see more of my work on Twitter or subscribe to my newsletter.

Stay In The Loop

Get updates when I share new posts, resources and tips.

NextUnderstanding Gradients