30% offEnding soon
useSwipeLoading saved progress…

useSwipe

A swipe is a drag that gets classified into a single direction — left, right, up, or down — once it has traveled far enough to count. useSwipe watches pointer events on an element and reports that direction, turning a raw drag into the gesture that flips a carousel slide, dismisses a card, or opens a drawer. It builds on the unified Pointer Events API, so one code path covers mouse, touch, and pen.

Implement useSwipe(options). It returns a [bond, state] pair: spread bond on the element to start tracking on pointer down, and read state.direction and state.swiping. Two decisions turn a drag into a swipe — which axis dominates, and whether the drag passed a distance threshold.

Signature

function useSwipe(options) {
  // options: { threshold = 50, onSwipe(direction, event) }
  // returns [bond, state]
  //   bond:  { onPointerDown }              spread on the element
  //   state: { direction, swiping }         direction is left|right|up|down|null
}

Examples

const [bond, state] = useSwipe({ onSwipe: (dir) => console.log(dir) });
// spread {...bond} on an element; drag right 80px and down 30px, then release
// logs 'right' — the bigger axis wins, so a diagonal is still one direction
const [bond, state] = useSwipe({ threshold: 80 });
// a 60px drag leaves state.direction as null — it didn't travel far enough
// a 90px drag to the right sets state.direction to 'right'

Notes

  • Dominant axis — a real drag is never purely horizontal or vertical. Pick the direction from the bigger delta: when |dx| is greater than |dy| it is horizontal (right/left), otherwise vertical (down/up). One drag yields one direction, never two.
  • Threshold — a swipe only counts once the dominant delta passes threshold (default 50px). A shorter move is a tap: direction stays null and onSwipe does not fire.
  • Listen on window — attach pointermove and pointerup to window, not the element, so a fast swipe that leaves the element still delivers its finish. Remove them on pointer up and on unmount.
  • Stable bond — spreading bond should not re-attach handlers every render. Keep its identity stable and read a fresh onSwipe from a ref.
  • swipingtrue from pointer down until release, so the element can be styled mid-gesture. You do not need velocity, multi-touch, or scroll-locking here.