Gestures are a very important aspect of user interfaces; they make the elements feel alive. The user performs an action and the interface responds appropriately.
Gestures in motion
We interact with interfaces all the time, hovering over buttons, tapping links, and dragging cards. Movement extends React’s core event system with a simple set of gestures that make these interactions feel alive.
To use gestures in motion, we need to use the motion component. It supports hover, tap, pan, drag, focusAnd inView gestures.
whileHover
whileTap
onPan
whileDrag
whileFocus
whileInView
Each of these gestures can trigger animations while- props or handled manually with event listeners.
Float
Hover is the most obvious gesture, but still worth mentioning. whileHover detects when a pointer enters or leaves the element.
The great thing about Motion is that you can easily use feather animations for all these gestures, which is quite 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. Personally, I prefer Motion because I can use springs, which create a more natural-looking movement.
Performance is worth keeping in mind, but for effects involved 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 discussed this a bit in my will-change in CSS article.
In most cases there is no meaningful performance difference. Sometimes Motion’s performance is even better, because it handles independent transformations more efficiently than CSS variables.
Crane
The whileTap prop fires when a pointer or keyboard key presses and releases the same element.
Tapping is automatically canceled if the pointer moves too far, so it doesn’t conflict with dragging.
<motion.div
whileTap={{ scale: 0.8 }}
transition={{ type: "spring", duration: 0.5, bounce: 0 }}
/>It is also keyboard accessible. Pressing Enter will activate this onTapStart And whileTapreleasing Enter fires onTap.
Drag
The drag gesture applies the movement of the pointer directly to a component. Motion offers many controls as standard. To make a component draggable you can simply use the drag gag for one motion component.
You can then add whileDrag and the home you want to animate. In this example I also added dragConstraintsso that the element cannot be dragged outside the 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 Movement documentation include many more use cases and configurations.
Liquid glass
Apple introduces a beautiful and elegant new software design.
Pan
The pan Recognize gestures when a pointer moves more than 3 pixels when pressed, ending when released.
Pan has no associate while- prop, so you need to use event handlers like onPan, onPanStart And onPanEnd.
<motion.div
onPanStart={() => setIsPanning(true)}
onPanEnd={() => setIsPanning(false)}
animate={{ scale: isPanning ? 0.9 : 1 }}
/>Going back to the spring animations, a scrolling component like this is one place where I think it’s definitely worth using spring animations because the movement here looks natural.
Focus
The whileFocus prop fires when an element gains or loses focus. It follows the same logic as :focus-visible in CSS.
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’s great for elements like buttons, input fields, and others where you want a clear visual response when focused.
Because you can animate virtually any value, you can get really creative with implementing even more complex effects.
However, I recommend keeping focus animations subtle. Overly complex movements can feel distracting or even disorienting when navigating the keyboard.
Insight
whileInView is fired when an element enters or leaves the viewport.
It’s great for scroll-based reveals or progressive section entries. You can also combine it with the initial prop to animate the component to and from 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 is the initial (unrevealed) value of the blur filter 8px and once the part is in view, it is animated 0px. There are lots of great examples and creative ways to use this in the Movement documentation.
Conclusion
Many of the things covered here can be found in the Movement documentation.
By no means is the intention of this article to just recite things written in the documentation, but instead point out that this is something available with Motion, show how I use it in my work, and show examples of where I use them.
Big thanks to Matt Perry for providing feedback on this article.
More
If you have any questions, you can reach me at jakub@kbo.sk, see more of my work at Tweet or subscribe to my newsletter.
Stay informed
Receive updates when I share new posts, resources, and tips.
#Movement #gestures


