mrkeyoor.com_
Tue 22 Sept 22:33 UTC
npmWeb Frontendupdated 22 Sept 2026

@use-gesture/react review

@use-gesture/react 10.3.1 converts pointer, touch, wheel, scroll, hover, move, and Safari gesture events into React hook state. Handlers receive displacement, persistent offset, velocity, direction, swipe decisions, pinch scale, timing, bounds, and cancellation controls. `useGesture` can combine recognizers on one target. The package recognizes input only: it does not animate elements, reorder lists, announce drag-and-drop state to assistive technology, or supply a finished interaction component.

Verdict

@use-gesture/react 10.3.1 added 11.9 KB gzipped in our browser build and installed in 1.7 seconds with 0 audit findings. It earns that cost for custom multi-input surfaces, but it is the wrong dependency for sortable accessibility or a simple click-and-drag element.

We installed it

Lab card: what happened when we installed @use-gesture/reactScreenshot of @use-gesture/react documentation
Install✓ · 1.7s3 packages on disk · 2 MB
ImportESM import works · require() works · CommonJS package
Browser11.9 KBgzipped (36.8 KB minified), bundled with esbuild
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does @use-gesture/react install cleanly?

Yes. In a fresh container with an empty cache, npm install @use-gesture/react finished in 2 seconds, leaving 3 packages and 2 MB on disk. npm audit reported no known vulnerabilities.

How much does @use-gesture/react add to a browser bundle?

11.9 KB gzipped (36.8 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does @use-gesture/react work with both ESM and CommonJS?

Yes. Both import '@use-gesture/react' and require('@use-gesture/react') worked in Node 22 in our run. The package is published as CommonJS.

Does @use-gesture/react include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

@use-gesture/react or @dnd-kit/core: which should you use?

@dnd-kit/core: Choose it for sortable or drag-and-drop UI with keyboard sensors, collision detection, and accessibility primitives. @use-gesture/react 10.3.1 added 11.9 KB gzipped in our browser build and installed in 1.7 seconds with 0 audit findings.

When should you not use @use-gesture/react?

You are building a sortable or accessible drag-and-drop workflow. The package has no collision engine, keyboard sensors, reorder model, or live-region announcements; dnd-kit is a better match.

API stability4/5Version 10 uses one recognizable hook family and a shared state vocabulary for `movement`, `offset`, velocity, direction, activity, and memo values. Gesture-specific configuration follows the same handler-plus-options shape. The previous major did change packaging and configuration, and no release after March 2024 provides fresh evidence against newer React or browser event changes.
Docs4/5The dedicated site has separate references for each recognizer, common state, gesture options, pointer capture, passive events, `touch-action`, Safari pinch handling, and frequent mistakes. Interactive examples make coordinate behavior easier to inspect. Animation-heavy demos can blur the ownership line, so readers must remember that React Spring or Motion is producing pixels while this package reports input.
Maintenance2/5npm still serves 10.3.1 from March 2024. GitHub reports 9,622 stars, 54 open issues and pull requests, an unarchived repository, and a last push on July 15, 2024. Wide use and stable docs reduce immediate concern, but two quiet years is meaningful for code coupled to React hooks and changing browser input behavior.
Ecosystem4/5npm counted 6,572,876 downloads in the week ending August 24, 2026. The package works with React 16.8 or newer, shares a core with the vanilla target, and is commonly paired with React Spring in the pmndrs community. Integrations remain recipes rather than complete controls, animation output, sortable behavior, or accessibility policy.

Use it if

  • A canvas, map, card, scrubber, slider, or zoom surface needs direct manipulation across mouse and touch input.
  • Handlers need normalized offset, velocity, direction, swipe, pinch, tap, and first or last event state.
  • Several gesture types must share one target without hand-maintained browser listeners.
  • Bounds, axis locking, thresholds, rubberbanding, pointer capture, or deliberate cancellation are part of the interaction design.
Skip it if

Setup reality

We installed @use-gesture/react 10.3.1 in a clean Node 22 Bookworm sandbox. npm completed in 1.7 seconds, left 3 packages using 2 MB, and found 0 vulnerabilities at every severity. The package has 1 direct dependency, 1 React peer, and 156 KB unpacked. Our inspection found no TypeScript declarations in the installed package.

The package is CommonJS without an exports map. Both require() and ESM import worked under Node 22.23.2. A browser esbuild run produced 36.8 KB minified and 11.9 KB gzipped. That size covers recognition code, not the animation library or interaction UI usually paired with it.

Mobile behavior begins with CSS. A two-axis draggable surface often needs touch-action: none; a horizontal control that should preserve page scrolling may need pan-y. Without a deliberate value, the browser can take over and cancel pointer delivery. Links and images also bring selection, navigation, and native dragging, so combine filterTaps, preventDefault, and CSS only where the product behavior justifies them.

React prop bindings cannot request non-passive listeners. Wheel cancellation or Safari trackpad pinch may require a stable target plus eventOptions: { passive: false }. Drag captures the pointer by default, which follows movement outside the element but blocks other targets until release. movement resets per gesture, while offset persists. Rubberbanding does nothing without bounds, and wheel or scroll endings are inferred through debounce rather than a DOM end event.

Patterns

Move an element from persistent offset drag-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` carries position between drags. Use `movement` when every new gesture should begin at zero.

Return an element after release snap-back-drag

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

`movement` resets for each drag, which fits temporary displacement followed by a spring back to origin.

Clamp and rubberband a drag constrain-drag

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

Rubberbanding needs bounds and settles at the nearest allowed value after release.

Choose the detected drag axis lock-drag-axis

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

`axis: 'lock'` waits for intent before selecting x or y. Use a fixed axis when the component must never switch.

Treat short drags as taps distinguish-tap

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

`filterTaps` holds drag behavior until movement passes the configured tap threshold.

Recognize a release-time swipe detect-swipe

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

A swipe value appears only when its distance, velocity, and duration tests all pass.

Scale and rotate from pinch state pinch-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 binding and browser gesture handling because React does not expose those proprietary events.

Attach drag and pinch together combine-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 } },
  },
)

With `useGesture`, each recognizer keeps its options under its own config key.

Watch a browser scroll target bind-window-scroll

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

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

Do not touch `window` during server rendering. Mount this listener in the browser or attach it through a post-mount ref.

Cancel an excessive drag cancel-drag

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

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

Alternatives

PackageRegistryPick it when
@dnd-kit/corenpmChoose it for sortable or drag-and-drop UI with keyboard sensors, collision detection, and accessibility primitives.
react-draggablenpmChoose it when one constrained draggable element and a component API are enough.
framer-motionnpmChoose it when gesture handling should arrive inside a wider animation and layout system.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.