Gesture options

React-use-gesture offers different options to configure the gestures. Some options are generic to the way React-use-gesture behaves while some other options can be configured per gesture.

Structure of the config object

Depending on whether you use gesture-specific hooks or if you use the useGesture hook, you'll need to structure the config option object differently.

// when you use a gesture-specific hook
useDrag(state => doSomethingWith(state), { ...genericOptions, ...dragOptions })
// when you use the useGesture hook
useGesture(state => doSomethingWith(state), {
drag: dragOptions,
wheel: wheelOptions,
pinch: pinchOptions
// etc.

Generic options

Generic options deal with how React-use-gesture will set event listeners.

OptionTypeDefault valueDescription
domTargetnode|RefundefinedLets you specify a dom node or React ref you want to attach the gesture to.
eventOptions.capturebooleanfalseIf true, events will be captured.
eventOptions.passivebooleantrueSets whether events are passive. Note that if you want events not to be passive, you will need to use domTarget because of the way React handles events.
eventOptions.pointerbooleanfalseIf true, gestures will use pointer events instead of mouse or touch events where possible.
windownodewindowLets you specify which window element the gesture should bind events to (only relevant for the drag gesture).
enabledbooleantrueWhen set to false none of your handlers will be fired.

Gesture common options

All gestures share a set of common options described below.

OptionTypeDefault valueDescription
enabledbooleantrueWhether the gesture is enabled.
initialvector|() => vector[0,0]The initial position movement should start from.
thresholdnumber|vectorundefinedThe handler will fire only when the gesture displacement is greater than the threshold.
rubberbandboolean|number|vector0The elasticity coefficient of the gesture when going out of bounds. When set to true, the elasticiy coefficient will be defaulted to 0.15

xy gestures specific options

Here is the list of options for drag, move, wheel and scroll gestures:

OptionTypeDefault valueDescription
axisx|yundefinedYour handler will only trigger if a movement is detected on the specified axis.
lockDirectionbooleanfalseIf true, the gesture will lock the first detected direction.
bounds{top,bottom,left,right}InfinityLimits the gesture movement and offset to the specified bounds.

pinch specific options

OptionTypeDefault valueDescription
distanceBounds{min,max}InfinityLimits the distance movement and offset to the specified bounds.
angleBounds{min,max}InfinityLimits the angle movement and offset to the specified bounds.

drag specific options

On top of the options for xy gestures seen above, drag has the following additional options:

OptionTypeDefault valueDescription
filterTapsbooleanfalseIf true, the component won't trigger your drag logic if the user just clicked on the component.
delayboolean|number0If set, the handler will be delayed for the duration of the delay (in ms) — or if the user starts moving. When set to true, delay is defaulted to 180ms.
swipeDistancenumber|vector[60,60]The minimum distance per axis (in pixels) the drag gesture needs to travel to trigger a swipe.
swipeVelocitynumber|vector[0.5,0.5]The minimum velocity per axis (in pixels / ms) the drag gesture needs to reach before the pointer is released.

Options explained


React-use-gesture also supports adding handlers to dom nodes directly (or the window or document objects). In that case, you shouldn't spread the bind() object returned by the hooks as a prop, but use the React.useEffect hook as below.

function ScrollExample() {
const [{ width }, set] = useSpring(() => ({ width: '0%' }))
const height = document.documentElement.scrollHeight
const bind = useScroll(
({ xy: [, y] }) => set({ width: (y / height) * 100 + '%' }),
{ domTarget: window }
React.useEffect(bind, [bind])
return <animated.div style={{ width }} />

The code above binds the scroll gesture to the document window, and acts as a scroll indicator. Try scrolling the page and you'll see the blue bar progress.

You can also directly pass a ref to domTarget. This is actually usefull when you want your events not to be passive.

const myRef = React.useRef(null)
// This will add a scroll listener the div
const bind = useScroll(({ event }) => event.preventDefault(), {
domTarget: myRef,
eventOptions: { passive: false }
React.useEffect(bind, [bind])
return <div ref={myRef} />


Everytime a gesture starts, the movement state attribute is set to [0, 0]. But in some cases, you might want to calculate movement from an initial position that is external to your logic1.

Let's take a tangible example: say that a draggable component turns back to its initial position slowly. In the meantime, the draggable component should still be interruptible at any moment. In that case, you can use initial to set the position of the component at the moment the user drags it to the value of the spring.

Drag the blue square and before it goes back to its origin drag it again. If you've unticked the checkbox, you'll notice that the square goes back to its origin instead of moving from where you've dragged it: that's because movement is by default reset to [0, 0].

The code below shows how the example works:

function InitialExample() {
const [{ x }, set] = useSpring(() => ({ x: 0 }))
const bind = useDrag(
({ down, movement: [mx] }) =>
set({ x: down ? mx : 0, immediate: down, config: { duration: 3000 } }),
{ initial: () => [x.get(), 0] }
return <animated.div {...bind()} style={{ x }} />

Unless your initial position is static or depends on state, make sure you use a function rather than a static array.


By default, your gesture handler will be triggered as soon as an event is fired. However, there are situations where you want to make sure the user action is intentional: that's where threshold comes into play.

threshold is the minimum displacement the gesture movement needs to travel before your handler is fired.

x: 100 px
y: 100 px

In this example, we've set the threshold to 1002 and made visible when that threshold is exceeded: when you start dragging the blue square, you'll see a ghost square showing how many pixels are left until the blue square starts moving per axis3.

function TresholdExample() {
const [{ x, y }, set] = useSpring(() => ({ x: 0, y: 0 }))
const bind = useDrag(({ offset: [x, y] }) => set({ x, y }), {
threshold: 10
return <animated.div {...bind()} style={{ x, y }} />


If you want to set contraints to the user gesture, then you should use the bounds option. In that case, both the gesture movement and offset will be clamped to the specified bounds. bounds will be defaulted to Infinity when not set.

function BoundsExample() {
const [{ x, y }, set] = useSpring(() => ({ x: 0, y: 0 }))
const bind = useDrag(
({ down, offset: [ox, oy] }) => set({ x: ox, y: oy, immediate: down }),
{ bounds: { left: -100, right: 100, top: -50, bottom: 50 } }
return <animated.div {...bind()} style={{ x, y }} />

distanceBounds and angleBounds serve the same purpose as bounds for the pinch gesture, in a {min,max} format.


In some cases, you may want to simulate resistance when the user drags a component, for example when the end of a content is reached4.

You can set rubberband to true to use the default elasticity coeffecient of 0.15, or specify your own. The rubberband option also accepts a vector if you want to set different elasticity coeffecients per axis.

function RubberbandExample() {
const [{ x, y }, set] = useSpring(() => ({ x: 0, y: 0 }))
const bind = useDrag(
({ down, offset: [ox, oy] }) => set({ x: ox, y: oy, immediate: down }),
bounds: { left: -100, right: 100, top: -50, bottom: 50 },
rubberband: true
return <animated.div {...bind()} style={{ x, y }} />

Note that you have to set bounds for rubberbanding to take effect.

If you stop your gesture while being off-bounds, the offset or movement for the last event will be reverted to the closest bounds.

axis (xy gestures only)

axis makes it easy to constraint the user gesture to a specific axis.

function AxisExample() {
const [{ x }, set] = useSpring(() => ({ x: 0 }))
const bind = useDrag(
({ down, movement: [mx] }) => set({ x: down ? mx : 0 }),
{ axis: 'x' }
return <animated.div {...bind()} style={{ x }} />

From the code below it isn't obvious to understand why axis might be useful, since in any case the y movement isn't part of the logic.

But in reality axis does slightly more than just locking the gesture direction: if it detects that the user intent is to move the component in a different direction, it will stop firing the gesture handler. Here is an example to show the difference.

The component above can only move along the x axis. But try dragging the component on the vertically. Without the axis option, you should notice the component movement will slightly jiggle horizontally because you're movement won't be perfectly vertical.

lockDirection (xy gestures only)

lockDirection allows you to lock the movement of the gesture once a direction has been detected. In other words, if the user starts moving horizontally, the gesture will be locked on the x axis.

function LockDirectionExample() {
const [{ x, y }, set] = useSpring(() => ({ x: 0, y: 0 }))
const bind = useDrag(
({ down, movement: [mx, my] }) => {
set({ x: down ? mx : 0, y: down ? my : 0, immediate:down })
{ lockDirection: true }
return <animated.div {...bind()} style={{ x, y }} />

filterTaps (drag only)

Making a draggable component tappable or clickable can be tricky: differenciating a click from a drag is not always trivial. When you set filterTaps to true, the tap state attribute will be true on release if the total displacement is inferior to 3 pixels while down will remain false all along.

status: idle
function FilterTapsExample() {
const [{ x, y }, set] = useSpring(() => ({ x: 0, y: 0 }))
const bind = useDrag(
({ down, movement: [mx, my], tap }) => {
if (tap) alert('tap!')
set({ x: down ? mx : 0, y: down ? my : 0 })
{ filterTaps: true }
return <animated.div {...bind()} style={{ x, y }} />

delay (drag only)

delay delays the drag gesture for the amount of milliseconds you specify. This might be useful if you don't want your logic to fire right away. The below example has a delay set to 1000. Try clicking on the square without moving your mouse.

1000ms before drag starts

Note that if the the pointer is moved by the user, the drag gesture will fire immediately without waiting for the delay.

function DelayExample() {
const [{ x, y, scale }, set] = useSpring(() => ({ x: 0, y: 0, scale: 1 }))
const bind = useDrag(
({ down, movement: [mx, my] }) =>
set({ x: down ? mx : 0, y: down ? my : 0, scale: down ? 1.2 : 1 }),
{ delay: 1000 }
return <animated.div {...bind()} style={{ x, y, scale }} />

Note that delay and threshold don't play well together: without moving your pointer, your handler will never get triggered.

  1. If you're used to React-use-gesture, this was the most common usecase for memo.
  2. This is a bit extreme in actual use cases you would be closer to 20.
  3. As you might have noticed from the example above, threshold works per axis: if the gesture exceeds the threshold value horizontally, you will get updates for horizontal displacement, but vertical threshold will have to be reached before vertical displacement is registered.
  4. Have a look at this article for more details about building mobile interfaces.