Skip to main content
Version: next

useGesture

useGesture attaches a Gesture.* descriptor to an element, window, or an array of elements. Config and handlers stay live across renders, so editing them after mount takes effect without re-attaching listeners.

useGesture(ref, descriptor);
useGesture(window, descriptor);
useGesture(refs, descriptor | ((index: number) => descriptor));

Every Gesture.*() call returns a chainable builder. Call .onStart(), .onChange(), .onEnd() (and, where relevant, .onFinalize() or .onSwipe()) plus any config methods, then pass the built descriptor to useGesture.

Gesture.Pan()

Streams a full press-move-release cycle. useDrag is built on this. Reach for Gesture.Pan() directly when you need drag data without useDrag's built-in spring/decay behavior.

import { useRef } from 'react';
import { Gesture, useGesture, useValue, animate } from 'react-ui-animate';

function Panner() {
const ref = useRef(null);
const [x, setX] = useValue(0);

/* Stream pan movement into an animated value */
useGesture(
ref,
Gesture.Pan()
.onChange(({ down, movement }) => setX(movement.x))
.onEnd(() => setX(0))
);

return <animate.div ref={ref} style={{ translateX: x }} />;
}

Config: .enabled(bool), .minDistance(px), .axis('x' | 'y'). Event shape: { phase, down, movement, offset, velocity, event, target, cancel }.

Preview

Gesture.Move()

Tracks pointer position with no press required: cursor-follow effects, magnetic hover.

useGesture(
window,
Gesture.Move().onChange(({ offset }) => setCursor(offset))
);

Event shape: { movement, offset, velocity, event, cancel }.

Preview

Gesture.Wheel()

Trackpad/mouse-wheel deltas, for custom zoom or horizontal scroll.

useGesture(
ref,
Gesture.Wheel().onChange(({ movement }) => setZoom((z) => z - movement.y * 0.001))
);

Event shape: { movement, offset, velocity, event, cancel }.

Preview

Gesture.Scroll()

Native scroll events with movement/velocity, for scroll-linked animation and progress bars. (For intersection-based reveal-on-scroll, see useInView or the view prop instead: Gesture.Scroll() is for continuous scroll position, not enter/exit detection.)

useGesture(
window,
Gesture.Scroll().onChange(({ offset }) => {
const max = document.documentElement.scrollHeight - window.innerHeight;
setProgress(offset.y / max);
})
);

Event shape: { movement, offset, velocity, event, cancel }.

Preview

Gesture.Swipe()

A fast, short, directional flick resolved once at release. Not a stream: only onSwipe fires, and only if the release clears both thresholds.

useGesture(
ref,
Gesture.Swipe()
.distanceThreshold(40)
.onSwipe(({ direction }) => {
if (direction === 'left') goNext();
if (direction === 'right') goBack();
})
);

Config: .enabled(bool), .axis('x' | 'y'), .velocityThreshold(pxPerMs) (default 0.5), .distanceThreshold(px) (default 30). Event shape: { direction, movement, velocity, event, target }.

Preview

Gesture.Hover()

Enter/leave/move-while-over: deliberately leaner than Gesture.Move() (no movement/velocity). Use Gesture.Move() instead if you need those while hovering.

useGesture(
ref,
Gesture.Hover().onChange(({ hovering }) => setIsHovering(hovering))
);

Event shape: { hovering, offset, event, target }.

Preview

Gesture.Pinch()

Two-finger pinch-to-zoom. scale is the current pointer-pair distance relative to where the gesture began.

useGesture(
ref,
Gesture.Pinch().onChange(({ scale }) => setZoom(scale))
);

Config: .enabled(bool), .threshold(v) (minimum |scale - 1| before recognized as active, default 0.02). Event shape: { phase, scale, velocity, center, event, target, cancel }.

Preview

Gesture.Rotate()

Two-finger rotation, in degrees from the pointer pair's starting angle. Gesture.Pinch() and Gesture.Rotate() can both be registered on the same ref to read scale and rotation from one two-finger gesture.

useGesture(
ref,
Gesture.Rotate().onChange(({ rotation }) => setAngle(rotation))
);

Config: .enabled(bool), .threshold(degrees) (minimum rotation before recognized as active, default 2). Event shape: { phase, rotation, velocity, center, event, target, cancel }.

Preview

Array mode

Passing an array of refs registers each one individually, diffed by ref identity (not array length). Reordering or adding/removing refs doesn't tear down unrelated registrations. Every emitted event gets index merged in:

useGesture(
cardRefs,
(index) => Gesture.Pan().onChange(({ movement, index }) => setX(index, movement.x))
);