useSwipe()
Last updated: 24/04/2026
Overview
useSwipe listens to touchstart and touchend on window, records the first touch’s clientX / clientY, and on end computes dx / dy. If the larger axis exceeds threshold (default 30 CSS pixels), the stronger axis becomes the swipe: horizontal gives left or right, vertical gives up or down; otherwise direction stays null. While a finger is down, isSwiping is true; after touchend, isSwiping is false and direction holds the last result for that gesture. The API is touch only; mouse drags and pointer swipes are not included.
What it accepts
- Optional
threshold(default 30): minimum travel to count as a swipe, in the same units asclient*.
What it returns
direction:'left' | 'right' | 'up' | 'down' | nullisSwiping:boolean
Usage
Use a higher threshold to ignore tiny jitters. Show last direction and whether a touch is in progress (test on a touch device).
tsx
import useSwipe from '@dedalik/use-react/useSwipe'
function Example() {
const { direction, isSwiping } = useSwipe(64)
return (
<div>
<p>Threshold: 64 px (custom)</p>
<p>Swiping: {isSwiping ? 'yes' : 'no'}</p>
<p>Last direction: {direction ?? '-'}</p>
</div>
)
}
export default function Demo() {
return <Example />
}API Reference
useSwipe
Signature: useSwipe(threshold = 30): UseSwipeState
Parameters
threshold(optional) - Min delta on the dominant axis. Default 30.
Returns
direction- Cardinal swipe ornullisSwiping- True betweentouchstartandtouchend
Copy-paste hook
TypeScript
tsx
import { useEffect, useState } from 'react'
export type SwipeDirection = 'left' | 'right' | 'up' | 'down' | null
export interface UseSwipeState {
direction: SwipeDirection
isSwiping: boolean
}
/**
* Detects swipe direction from touch events.
*/
export default function useSwipe(threshold = 30): UseSwipeState {
const [state, setState] = useState<UseSwipeState>({ direction: null, isSwiping: false })
useEffect(() => {
if (typeof window === 'undefined') return
let startX = 0
let startY = 0
const onStart = (event: TouchEvent) => {
const touch = event.touches[0]
if (!touch) return
startX = touch.clientX
startY = touch.clientY
setState({ direction: null, isSwiping: true })
}
const onEnd = (event: TouchEvent) => {
const touch = event.changedTouches[0]
if (!touch) return
const dx = touch.clientX - startX
const dy = touch.clientY - startY
const absX = Math.abs(dx)
const absY = Math.abs(dy)
let direction: SwipeDirection = null
if (absX >= threshold || absY >= threshold) {
if (absX > absY) {
direction = dx > 0 ? 'right' : 'left'
} else {
direction = dy > 0 ? 'down' : 'up'
}
}
setState({ direction, isSwiping: false })
}
window.addEventListener('touchstart', onStart, { passive: true })
window.addEventListener('touchend', onEnd, { passive: true })
return () => {
window.removeEventListener('touchstart', onStart)
window.removeEventListener('touchend', onEnd)
}
}, [threshold])
return state
}JavaScript
js
import { useEffect, useState } from 'react'
/**
* Detects swipe direction from touch events.
*/
export default function useSwipe(threshold = 30) {
const [state, setState] = useState({ direction: null, isSwiping: false })
useEffect(() => {
if (typeof window === 'undefined') return
let startX = 0
let startY = 0
const onStart = (event) => {
const touch = event.touches[0]
if (!touch) return
startX = touch.clientX
startY = touch.clientY
setState({ direction: null, isSwiping: true })
}
const onEnd = (event) => {
const touch = event.changedTouches[0]
if (!touch) return
const dx = touch.clientX - startX
const dy = touch.clientY - startY
const absX = Math.abs(dx)
const absY = Math.abs(dy)
let direction = null
if (absX >= threshold || absY >= threshold) {
if (absX > absY) {
direction = dx > 0 ? 'right' : 'left'
} else {
direction = dy > 0 ? 'down' : 'up'
}
}
setState({ direction, isSwiping: false })
}
window.addEventListener('touchstart', onStart, { passive: true })
window.addEventListener('touchend', onEnd, { passive: true })
return () => {
window.removeEventListener('touchstart', onStart)
window.removeEventListener('touchend', onEnd)
}
}, [threshold])
return state
}