npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@msobiecki/react-marauders-path

v1.32.0

Published

A lightweight, type-safe React library for handling keyboard and pointer events in a unified way.

Downloads

3,394

Readme

react-marauders-path

License

A lightweight, type-safe React library for handling keyboard, pointer, mouse, wheel, and gesture events including tap, double-tap, press, swipe, drag, and pinch interactions.

react-marauders-path

Features

  • 🎹 Keyboard Event Handling - Detect single keys, key combinations, and sequences with configurable timing thresholds
  • 🖱️ Pointer Event Handling - Detect pointer interactions events with pointer type filtering
  • 🖱️ Mouse Event Handling - Detect mouse interactions through a pointer-powered mouse events with button filtering
  • 🖱️ Mouse Wheel Event Handling - Detect mouse wheel delta values with optional requestAnimationFrame batching for smoother updates
  • 👐 Gesture Event Handling - Detect tap, double-tap, press, swipe, drag, and pinch gestures
    • 👆 Tap Gesture Handling - Detect single taps or clicks with configurable movement and duration thresholds
    • 👆👆 Double-Tap Gesture Handling - Detect consecutive taps or clicks with configurable timing and position thresholds
    • Press Gesture Handling - Detect press-and-hold interactions with configurable delay and movement thresholds
    • 🖐️ Swipe Gesture Handling - Detect directional swipes with configurable distance, velocity, and pointer type filtering
    • Drag Gesture Handling - Detect movement, deltas, duration, and start/end positions with pointer type filtering and optional requestAnimationFrame batching
    • 🤏 Pinch Gesture Handling - Detect two-finger distance, delta, and scale with pointer type filtering and optional requestAnimationFrame batching

Installation

npm install @msobiecki/react-marauders-path

Quick Start

Key Event Hook

Single Key Pattern

import { useKey } from '@msobiecki/react-marauders-path';

function MyComponent() {
  useKey('a', (event, key) => {
    console.log(`Pressed ${key}`);
  });

  return <div>Press 'a'</div>;
}

Multiple Single Key Patterns

useKey(["a", "b", "c"], (event, key) => {
  console.log(`Pressed ${key}`);
});

Key Combination Pattern

useKey("a+b", (event, key) => {
  console.log(`Pressed ${key}`);
});

Multiple Key Combination Patterns

useKey(["a+b", "c+d"], (event, key) => {
  console.log(`Pressed ${key}`);
});

Key Sequence Pattern

useKey("ArrowUp ArrowUp ArrowDown ArrowDown", (event, key) => {
  console.log(`Pressed ${key}`);
});

Multiple Key Sequence Patterns

useKey(
  ["ArrowUp ArrowUp ArrowDown ArrowDown", "ArrowLeft ArrowRight"],
  (event, key) => {
    console.log(`Pressed ${key}`);
  },
);

Pointer Event Hook

import { usePointer, PointerEventTypes } from '@msobiecki/react-marauders-path';

function MyComponent() {
  usePointer((event, type, data) => {
    console.log(`Pointer ${type} at X: ${data.x}, Y: ${data.y}`);
  }, {
    eventType: [PointerEventTypes.Down, PointerEventTypes.Move, PointerEventTypes.Up],
  });

  return <div>Use pointer input</div>;
}

Mouse Event Hook

import { useMouse, MouseEventTypes, MouseButtons } from '@msobiecki/react-marauders-path';

function MyComponent() {
  useMouse((event, type, button, data) => {
    console.log(`Mouse ${type} button ${button} at X: ${data.x}, Y: ${data.y}`);
  }, {
    eventType: [MouseEventTypes.Move, MouseEventTypes.Click, MouseEventTypes.DoubleClick],
    eventButtons: [MouseButtons.Left],
  });

  return <div>Use mouse input</div>;
}

Mouse Wheel Event Hook

import { useWheel } from '@msobiecki/react-marauders-path';

function MyComponent() {
  useWheel((event, data) => {
    console.log(`Scrolled - X: ${data.deltaX}, Y: ${data.deltaY}`);
  });

  return <div>Scroll to interact</div>;
}

Gesture Event Hook

import { useGesture } from '@msobiecki/react-marauders-path';

function MyComponent() {
  useGesture('tap', (event, data) => {
    console.log(`Tapped at X: ${data.x}, Y: ${data.y}`);
  }, {
    threshold: 8,
  });

  return <div>Tap to interact</div>;
}

Tap Event Hook

import { useTap } from '@msobiecki/react-marauders-path';

function MyComponent() {
  useTap((event, data) => {
    console.log(`Tapped at X: ${data.x}, Y: ${data.y}`);
  });

  return <div>Tap to interact</div>;
}

Double Tap Event Hook

import { useDoubleTap } from '@msobiecki/react-marauders-path';

function MyComponent() {
  useDoubleTap((event, data) => {
    console.log(`Double tapped at X: ${data.x}, Y: ${data.y}`);
  });

  return <div>Double tap to interact</div>;
}

Press Event Hook

import { usePress } from '@msobiecki/react-marauders-path';

function MyComponent() {
  usePress((event, data) => {
    console.log(`Pressed at X: ${data.x}, Y: ${data.y}`);
  });

  return <div>Press and hold to interact</div>;
}

Swipe Event Hook

import { useSwipe } from '@msobiecki/react-marauders-path';

function MyComponent() {
  useSwipe('left', (event, direction, data) => {
    console.log(`Swiped ${direction} with velocity ${data.velocity}`);
  });

  return <div>Swipe left</div>;
}

Drag Event Hook

import { useDrag } from '@msobiecki/react-marauders-path';

function MyComponent() {
  useDrag((event, data) => {
    console.log(`Dragged by X: ${data.deltaX}, Y: ${data.deltaY}`);
  });

  return <div>Drag to interact</div>;
}

Pinch Event Hook

import { usePinch } from '@msobiecki/react-marauders-path';

function MyComponent() {
  usePinch((event, data) => {
    console.log(`Pinch scale: ${data.scale}, delta: ${data.delta}`);
  });

  return <div>Pinch to zoom</div>;
}

API

useKey(keyEvent, callback, options?)

Hook for keyboard event handling with support for single keys, combinations, and sequences.

Parameters:

  • keyEvent: string | string[] - Single key, key combination, or key sequence to listen for
  • callback: (event: KeyboardEvent, key: string) => void | boolean - Called when a key event occurs
  • options?: UseKeyOptions - Optional configuration

Options:

interface UseKeyOptions {
  eventType?: "keyup" | "keydown"; // Default: 'keyup'
  eventRepeat?: boolean; // Default: false
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  sequenceThreshold?: number; // Default: 1000 (ms) - Timeout between sequence keys
  combinationThreshold?: number; // Default: 200 (ms) - Timeout between combination keys
  container?: RefObject<HTMLElement>; // Default: window
}

usePointer(callback, options?)

Hook for handling pointer events with configurable event types, pointer types, and listener options.

Parameters:

  • callback: (event: PointerEvent, type: PointerEventType, data: PointerData) => void | boolean - Called when a pointer event occurs
  • options?: UsePointerOptions - Optional configuration

Options:

interface UsePointerOptions {
  eventType?: PointerEventType[]; // Default: ["pointermove", "pointerup", "pointerdown"]
  eventPointerTypes?: PointerEventPointerType[]; // Default: ["touch", "mouse", "pen"]
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  container?: RefObject<HTMLElement>; // Default: window
}

Pointer Data:

interface PointerData {
  x: number; // Pointer X position
  y: number; // Pointer Y position
}

useMouse(callback, options?)

Hook for handling mouse-like events through pointer events with button filtering and synthesized click/double-click support.

Parameters:

  • callback: (event: MouseEvent, type: MouseEventType, button: MouseButton, data: MouseData) => void | boolean - Called when a mouse event occurs
  • options?: UseMouseOptions - Optional configuration

Options:

interface UseMouseOptions {
  eventType?: MouseEventType[]; // Default: ["mousemove", "mousedown", "mouseup", "click", "dblclick"]
  eventButtons?: MouseButton[]; // Default: [0, 1, 2]
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  container?: RefObject<HTMLElement>; // Default: window
}

Mouse Data:

interface MouseData {
  x: number; // Mouse X position
  y: number; // Mouse Y position
  button: 0 | 1 | 2 | 3 | 4; // Active mouse button
}

useWheel(callback, options?)

Hook for handling mouse wheel events with support for different delta modes and options.

Parameters:

  • callback: (event: WheelEvent, data: WheelData) => void | boolean - Called when a wheel event occurs
  • options?: UseWheelOptions - Optional configuration

Options:

interface UseWheelOptions {
  eventPassive?: boolean; // Default: true
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  container?: RefObject<HTMLElement>; // Default: window
  raf?: boolean; // Default: false - Use requestAnimationFrame for batching
}

Wheel Data:

interface WheelData {
  deltaX: number; // Delta X value
  deltaY: number; // Delta Y value
  deltaZ: number; // Delta Z value
  deltaMode: number; // Delta mode value
}

useGesture(gesture, callback, options?)

Hook for gesture event handling that delegates to one of the low-level gesture hooks.

Parameters:

  • gesture: "tap" | "doubletap" | "press" | "swipe" | "drag" | "pinch" - Gesture to bind (must stay the same between renders)
  • callback - Callback type is inferred from gesture
  • options? - Options type is inferred from gesture

Swipe-only option:

  • direction?: SwipeDirection - Optional direction for useGesture("swipe", ...); defaults to "both"

Example:

useGesture(
  "swipe",
  (event, direction, data) => {
    console.log(direction, data.velocity);
  },
  {
    direction: "horizontal",
    threshold: 40,
  },
);

useTap(callback, options?)

Hook for handling single tap/click interactions.

Parameters:

  • callback: (event: PointerEvent, data: TapData) => void | boolean - Called when a tap gesture is recognized
  • options?: UseTapOptions - Optional configuration

Options:

interface UseTapOptions {
  eventPointerTypes?: TapEventPointerType[]; // Default: ["touch", "mouse", "pen"]
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  threshold?: number; // Default: 8 (px) - Maximum movement allowed between pointerdown and pointerup
  maxDuration?: number; // Default: 250 (ms) - Maximum tap duration
  container?: RefObject<HTMLElement>; // Default: window
}

Tap Data:

interface TapData {
  x: number; // Tap pointerup X
  y: number; // Tap pointerup Y
}

useDoubleTap(callback, options?)

Hook for handling double-tap / double-click interactions.

Parameters:

  • callback: (event: PointerEvent, data: DoubleTapData) => void | boolean - Called when a double tap is recognized
  • options?: UseDoubleTapOptions - Optional configuration

Options:

interface UseDoubleTapOptions {
  eventPointerTypes?: DoubleTapEventPointerType[]; // Default: ["touch", "mouse", "pen"]
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  delay?: number; // Default: 300 (ms) - Maximum interval between taps
  threshold?: number; // Default: 8 (px) - Maximum distance between two tap positions
  container?: RefObject<HTMLElement>; // Default: window
}

Double Tap Data:

interface DoubleTapData {
  x: number; // Tap pointerup X
  y: number; // Tap pointerup Y
}

usePress(callback, options?)

Hook for handling press-and-hold interactions.

Parameters:

  • callback: (event: PointerEvent, data: PressData) => void | boolean - Called when a press delay completes
  • options?: UsePressOptions - Optional configuration

Options:

interface UsePressOptions {
  eventPointerTypes?: PressEventPointerType[]; // Default: ["touch", "mouse", "pen"]
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  delay?: number; // Default: 500 (ms) - Press-and-hold duration required
  threshold?: number; // Default: 8 (px) - Maximum movement allowed while holding
  container?: RefObject<HTMLElement>; // Default: window
}

Press Data:

interface PressData {
  x: number; // Pointerdown X at press start
  y: number; // Pointerdown Y at press start
}

useSwipe(swipe, callback, options?)

Hook for handling touch swipe gestures with configurable distance and velocity thresholds.

Parameters:

  • swipe: "left" | "right" | "up" | "down" | "horizontal" | "vertical" | "both" - Allowed directions to listen
  • callback: (event: PointerEvent, direction: SwipeDirection, data: SwipeData) => void | boolean - Called when a swipe event occurs
  • options?: UseSwipeOptions - Optional configuration

Options:

interface UseSwipeOptions {
  eventPointerTypes?: SwipeEventPointerType[]; // Default: ["touch", "mouse", "pen"]
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  threshold?: number; // Default: 50 (px) - Minimum travel distance
  velocity?: number; // Default: 0.3 (px/ms) - Minimum average speed
  container?: RefObject<HTMLElement>; // Default: window
}

Swipe Data:

interface SwipeData {
  deltaX: number; // Horizontal travel
  deltaY: number; // Vertical travel
  velocity: number; // Average speed (distance / duration)
  duration: number; // Swipe duration in ms
}

useDrag(callback, options?)

Hook for handling pointer drag gestures with configurable threshold and pointer types.

Parameters:

  • callback: (event: PointerEvent, data: DragData) => void | boolean - Called when a drag event occurs
  • options?: UseDragOptions - Optional configuration

Options:

interface UseDragOptions {
  eventPointerTypes?: DragEventPointerType[]; // Default: ["touch", "mouse", "pen"]
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  threshold?: number; // Default: 0 (px) - Minimum drag distance
  container?: RefObject<HTMLElement>; // Default: window
  raf?: boolean; // Default: false - Use requestAnimationFrame for batching
}

Drag Data:

interface DragData {
  deltaX: number; // Horizontal movement from drag start
  deltaY: number; // Vertical movement from drag start
  movementX: number; // Horizontal movement from previous drag event
  movementY: number; // Vertical movement from previous drag event
  duration: number; // Drag duration in ms
  startX: number; // Drag start X
  startY: number; // Drag start Y
  endX: number; // Drag end X
  endY: number; // Drag end Y
}

usePinch(callback, options?)

Hook for handling two-pointer pinch gestures with distance and scale tracking.

Parameters:

  • callback: (event: PointerEvent, data: PinchData) => void | boolean - Called when a pinch event occurs
  • options?: UsePinchOptions - Optional configuration

Options:

interface UsePinchOptions {
  eventPointerTypes?: PinchEventPointerType[]; // Default: ["touch"]
  eventCapture?: boolean; // Default: false
  eventOnce?: boolean; // Default: false
  eventStopImmediatePropagation?: boolean; // Default: false
  threshold?: number; // Default: 0 (px) - Minimum pinch distance change
  container?: RefObject<HTMLElement>; // Default: window
  raf?: boolean; // Default: false - Use requestAnimationFrame for batching
}

Pinch Data:

interface PinchData {
  distance: number; // Current distance between active pointers
  delta: number; // Distance change since previous pinch update
  totalDelta: number; // Distance change since pinch start
  scale: number; // Current scale ratio (distance / startDistance)
}

Advanced Examples

Using Options for Event Type and Propagation Control

useKey(
  "Enter",
  (event, key) => {
    handleSubmit();
  },
  {
    eventType: "keydown",
    eventStopImmediatePropagation: true,
    container: inputRef,
  },
);

Listening for Key Repeat

// Allow repeated key presses to trigger callback (useful for games)
useKey(
  "ArrowUp",
  (event, key) => {
    moveUp();
  },
  {
    eventType: "keydown",
    eventRepeat: true,
  },
);

Custom Thresholds for Sequences and Combinations

// Increase threshold for slower typists
useKey(
  "a b c",
  (event, key) => {
    console.log(`Sequence: ${key}`);
  },
  {
    sequenceThreshold: 2000, // 2 seconds between keys
  },
);

// Increase threshold for combination keys
useKey(
  "a+b",
  (event, key) => {
    console.log(`Combination: ${key}`);
  },
  {
    combinationThreshold: 1000, // 1 second window for simultaneous press
  },
);

Examples

Game Controls

See the Cube Game Example for a full implementation:

cd examples/cube-the-game
npm install
npm run dev

This example demonstrates:

  • Combined mouse, touch, and keyboard input

Development

Build

npm run build

Watch Mode

npm run dev

Lint

npm run lint

License

See LICENSE file for details.