@usefy/use-long-press
v0.25.1
Published
A React hook for long-press gestures with a time threshold and movement cancellation — mouse and touch, with onStart/onFinish/onCancel callbacks
Maintainers
Readme
Overview
useLongPress is part of the @usefy ecosystem — a collection of production-ready, TypeScript-first, SSR-safe React hooks. It recognises a long-press gesture — a pointer held down on an element for at least a time threshold — and returns a bind object of DOM handler props you spread onto the target: <button {...bind}>.
Features
bindobject — spread the memoized handlers onto any element; they're referentially stable so spreading never churns the element's listeners.- Time threshold — the
callbackfires only after the press is held forthresholdms (default400). - Movement cancellation — dragging farther than
moveThresholdpx (default10) from the down point cancels the press; passmoveThreshold: falseto disable. - Mouse + touch — one gesture for both inputs; the synthetic mouse events browsers emit after a touch are detected and ignored, so a touch long-press never double-fires.
- Lifecycle callbacks — optional
onStart,onFinish, andonCancel(with a{ reason }of"released"|"moved"), all kept in latest-refs so inline functions never destabilise the handlers. - Safe by default — the pending timer is cleared on release, cancel, and unmount; SSR-safe (no
window/documentaccess) and StrictMode/concurrent-safe. - TypeScript-first — full type inference and exported types.
- Tiny & tree-shakeable — published as its own package.
Installation
# npm
npm install @usefy/use-long-press
# yarn
yarn add @usefy/use-long-press
# pnpm
pnpm add @usefy/use-long-pressRequires React 18 or 19 (peerDependencies: "react": "^18.0.0 || ^19.0.0").
Quick Start
import { useLongPress } from "@usefy/use-long-press";
function DeleteButton() {
const bind = useLongPress(() => deleteItem(), { threshold: 600 });
return (
<button {...bind} style={{ touchAction: "none", userSelect: "none" }}>
Hold to delete
</button>
);
}API
const bind = useLongPress(callback, options?);
// <button {...bind}>Press and hold</button>Parameters
| Parameter | Type | Description |
| ---------- | --------------------------- | --------------------------------------------------------------------------- |
| callback | (event) => void | Fires once when the press reaches threshold. Receives the originating pointer-down event. |
| options | UseLongPressOptions | Optional configuration (see below). |
Options
| Option | Type | Default | Description |
| --------------- | ------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------- |
| threshold | number | 400 | Milliseconds the press must be held before callback fires. |
| moveThreshold | number \| false | 10 | Pixels the pointer may drift from the down point before cancelling (reason "moved"). false disables movement cancellation. |
| disabled | boolean | false | When true, the handlers are no-ops (read at press-start). |
| onStart | (event) => void | — | Fires when a valid press begins, before the timer starts. |
| onFinish | (event) => void | — | Fires when a completed long press is then released. |
| onCancel | (event, { reason }) => void | — | Fires when a press ends early — reason is "released" or "moved". |
Returns
bind — a stable object of DOM handler props: onMouseDown, onMouseUp, onMouseLeave, onMouseMove, onTouchStart, onTouchEnd, onTouchMove. Spread it onto the target element to wire up the whole gesture.
preventDefault caveat
React attaches onTouchStart/onTouchMove as passive listeners, so calling event.preventDefault() inside them will not stop scrolling or the long-press context menu (the browser ignores it). Prevent those with CSS on the target instead:
.long-press-target {
touch-action: none; /* stop scroll/pan while pressing */
user-select: none; /* stop text selection */
-webkit-touch-callout: none; /* stop the iOS callout menu */
}This hook deliberately never calls preventDefault.
Touch / mouse de-duplication
After a touch sequence, browsers emit synthetic mousedown/mouseup/click events. useLongPress timestamps the last touch event and ignores any mousedown that arrives within a short guard window, so a single touch long-press fires exactly once. Pure-mouse and pure-touch devices are unaffected.
Testing
📊 View Detailed Coverage Report (GitHub Pages) — 29 tests, 100% statement coverage.
License
MIT © mirunamu
This package is part of the usefy monorepo.
