mrkeyoor.com_
Sat 08 Aug 21:00 UTC
npmWeb Frontendupdated 08 Aug 2026

@use-gesture/react

@use-gesture/react turns browser pointer, touch, wheel, scroll, move, hover, and Safari gesture events into React hooks with a shared state model. Instead of manually calculating displacement, velocity, direction, pinch scale, swipe intent, start and end timing, or bounds, a handler receives those values on every update. It recognizes interaction; it does not animate pixels, reorder lists, or provide accessible drag-and-drop semantics, so it is usually paired with react-spring, Motion, or purpose-built UI logic.

Verdict

A strong recognizer for custom, high-touch interfaces when your team understands browser input events. Do not install it for sortable drag and drop or to avoid learning touch-action, passive events, and accessibility behavior.

API stability4/5Version 10 exposes a consistent family of hooks with the same handler-plus-config shape, and shared state such as movement, offset, velocity, first, last, active, and memo is documented across gestures. The v10 upgrade introduced the separate vanilla package and configuration changes, but the 10.x React API has since moved through patch releases without a new major contract.
Docs4/5The dedicated site explains each gesture, the complete state object, shared and gesture-specific options, touch-action, pointer capture, passive events, Safari pinch behavior, common mistakes, and interactive examples. It is detailed enough to debug real input problems, though some prose and examples contain dated references, and animation examples can obscure which behavior belongs to this package.
Maintenance2/5The package is not deprecated or archived and 10.3.1 remains widely installed, but npm dates that release to March 2024 and GitHub reports the repository's last push in July 2024. That is a long quiet period for browser-input code that sits across React and platform event changes, so adopters should verify unresolved device-specific issues before making it foundational.
Ecosystem4/5It has millions of weekly downloads, first-class React hooks, a matching vanilla package, TypeScript declarations, side-effect-free package metadata, and close association with the pmndrs and react-spring communities. The abstraction composes with any animation system, but integrations are mostly examples rather than batteries-included components, accessibility tooling, or opinionated layout primitives.

Use it if

  • You are building direct-manipulation UI such as a canvas, slider, map, card stack, scrubber, or pinchable surface
  • You need normalized movement, offset, velocity, direction, swipe, tap, and pinch state across input devices
  • You want several gestures on one element through useGesture without maintaining overlapping event listeners
  • You need bounds, axis locking, thresholds, rubberbanding, cancellation, or pointer-capture controls
Skip it if

Setup reality

Install @use-gesture/react and provide React 16.8 or newer; version 10.3.1 depends on the matching @use-gesture/core and ships its own declarations. There is no required animation peer, but that does not mean animation is solved. Gesture callbacks can fire for every input frame, so driving ordinary React state may rerender too much; use an imperative animation value, refs, or carefully scoped state for continuous movement. The first mobile surprise is CSS: a draggable surface needs touch-action that matches the interaction, commonly none for a small two-axis control or pan-y when vertical page scrolling must remain available. Without it, the browser can cancel pointer events when scrolling begins. Images and links also have native drag behavior, selection, and navigation, so upstream recommends user-select styling plus preventDefault and filterTaps where appropriate. React prop bindings cannot configure passive listeners. If a handler must cancel wheel, scroll, or Safari gesture events, attach through the target option and use eventOptions: { passive: false }; target should be a stable ref or EventTarget. Safari trackpad pinch uses proprietary GestureEvents that React does not expose, another reason to bind via target, and suppressing browser zoom has accessibility consequences. Drag captures the pointer by default, which keeps tracking outside the element but prevents other elements from receiving pointer events until release; set pointer.capture false for cross-target interactions. offset persists between gestures while movement resets each time, a distinction that commonly causes snapping. Bounds clamp those values, and rubberband has no effect without bounds. End handlers for wheel, scroll, and move are debounced because the DOM has no native end event. Hooks must still follow React's rules, refs are unavailable on the first render, and server-rendered components must keep browser-only targets out of server execution.

Patterns

Track a draggable elementdrag-element

import { useDrag } from '@use-gesture/react'
import { useSpring, animated } from '@react-spring/web'

function Draggable() {
  const [{ x, y }, api] = useSpring(() => ({ x: 0, y: 0 }))
  const bind = useDrag(({ offset: [x, y] }) => api.start({ x, y }))
  return <animated.div {...bind()} style={{ x, y, touchAction: 'none' }} />
}

offset persists across drags; movement resets to zero at the start of each new gesture.

Return to the origin on releasesnap-back-drag

const bind = useDrag(({ down, movement: [mx, my] }) => {
  api.start({ x: down ? mx : 0, y: down ? my : 0, immediate: down })
})

movement is the right state for a temporary displacement because it starts fresh for every drag.

Constrain and rubberband movementconstrain-drag

const bind = useDrag(
  ({ offset: [x, y] }) => api.start({ x, y }),
  {
    bounds: { left: -100, right: 100, top: -50, bottom: 50 },
    rubberband: true,
  },
)

Rubberbanding only works with bounds, and the final event returns the value to the nearest bound.

Lock a gesture to its detected axislock-drag-axis

const bind = useDrag(
  ({ offset: [x, y] }) => api.start({ x, y }),
  { axis: 'lock', threshold: 8 },
)

axis: lock chooses x or y after intent is detected; use axis: x when the component must always be horizontal.

Separate taps from dragsdistinguish-tap

const bind = useDrag(
  ({ tap, last }) => {
    if (last && tap) openItem()
  },
  { filterTaps: true },
)

filterTaps prevents the drag handler from acting like a drag before the configured tap displacement is exceeded.

Move between pages on a horizontal swipedetect-swipe

const bind = useDrag(
  ({ swipe: [swipeX] }) => {
    if (swipeX) setPage((page) => page - swipeX)
  },
  {
    axis: 'x',
    swipe: { distance: 50, velocity: 0.5, duration: 250 },
  },
)

A swipe is reported on release only when distance, velocity, and duration conditions all pass.

Track pinch scale and anglepinch-scale

import { usePinch } from '@use-gesture/react'

const bind = usePinch(
  ({ offset: [scale, angle] }) => api.start({ scale, rotateZ: angle }),
  {
    scaleBounds: { min: 0.5, max: 3 },
    rubberband: true,
  },
)

Safari trackpad pinch needs target-based binding and browser gesture suppression because React does not expose WebKit GestureEvents.

Combine drag and pinch handlerscombine-gestures

import { useGesture } from '@use-gesture/react'

const bind = useGesture(
  {
    onDrag: ({ offset: [x, y] }) => api.start({ x, y }),
    onPinch: ({ offset: [scale, angle] }) => api.start({ scale, rotateZ: angle }),
  },
  {
    drag: { bounds: containerRef },
    pinch: { scaleBounds: { min: 1, max: 4 } },
  },
)

useGesture config nests gesture-specific options under drag, pinch, wheel, scroll, or move keys.

Observe scrolling on a targetbind-window-scroll

import { useScroll } from '@use-gesture/react'

function ScrollProgress() {
  useScroll(({ xy: [, y] }) => setProgress(y), { target: window })
  return null
}

Access window only in the browser; server-rendered applications should guard this component or pass a ref after mount.

Cancel a drag past a limitcancel-drag

const bind = useDrag(({ active, movement: [mx], cancel }) => {
  if (mx > 200) cancel()
  api.start({ x: active ? mx : 0, immediate: active })
})

Only drag and pinch gestures implement cancel; calling it for move, scroll, wheel, or hover has no effect.

Alternatives

PackageRegistryPick it when
@dnd-kit/corenpmYou need accessible sortable or drag-and-drop UI with sensors, collision detection, and keyboard support
react-draggablenpmYou only need constrained element dragging and prefer a component-oriented API
framer-motionnpmYou want gestures and animation in one React package and accept a broader rendering abstraction