Skip to main content
Version: Next

useMove

The useMove hook listens to pointer movements(without needing a press or drag) and provides real-time feedback like movement delta, velocity, and cursor offset. It supports both HTML elements and the window object.

Syntax

useMove(
refs: RefObject<HTMLElement> | RefObject<HTMLElement>[] | window,
callback: (event: MoveEvent) => void
): void

Parameters

NameTypeDescription
Refs (required)RefObject<HTMLElement> or RefObject<HTMLElement>[] or WindowElement(s) or window to track pointer movement over.
Callback (required)(event:MoveEvent) => voidCalled on every move event and when the pointer leaves.

MouseEvent Signature

type MoveEvent = {
index: number;
movement: { x: number; y: number }; // Movement from start of gesture
offset: { x: number; y: number }; // Pointer offset inside element
velocity: { x: number; y: number }; // px/ms
event: PointerEvent; // Native pointer event
cancel?: () => void; // Cancels the gesture (optional)
};

Examples

1. Track Pointer Across Entire Window

This example creates a custom teal-colored cursor that follows the pointer anywhere on the page.

import React from 'react';
import { animate, useValue, useMove } from 'react-ui-animate';

const App = () => {
  const [pos, setPos] = useValue({ x: 0, y: 0 });

  useMove(window, function ({ event }) {
    setPos({ x: event.clientX, y: event.clientY });
  });

  return (
    <animate.div
      style={{
        width: 50,
        height: 50,
        backgroundColor: 'teal',
        borderRadius: 4,
        translateX: pos.x,
        translateY: pos.y,
        position: 'fixed',
        top: 0,
        left: 0,
        pointerEvents: 'none',
      }}
    />
  );
};

export default App;

2. Track Pointer Within a Specific Element

This example tracks pointer movements only inside a target element (the gray box).
The teal "cursor" still moves globally but updates its position only when the pointer is inside the target box.

import React, { useRef } from 'react';
import { animate, useValue, useMove } from 'react-ui-animate';

const App = () => {
  const ref = useRef<HTMLDivElement>(null);
  const [pos, setPos] = useValue({ x: 0, y: 0 });

  useMove(ref, function ({ event }) {
    setPos({ x: event.clientX, y: event.clientY });
  });

  return (
    <>
      <animate.div
        style={{
          width: 50,
          height: 50,
          backgroundColor: 'teal',
          borderRadius: 4,
          translateX: pos.x,
          translateY: pos.y,
          position: 'fixed',
          top: 0,
          left: 0,
          pointerEvents: 'none',
        }}
      />

      <div
        ref={ref}
        style={{
          width: 300,
          height: 200,
          backgroundColor: '#f1f1f1',
          borderRadius: 4,
          border: '1px solid #e1e1e1',
        }}
      />
    </>
  );
};

export default App;

3. Tracking Pointer Movement Across Multiple Elements

This example demonstrates how to use useMove with multiple DOM elements (using refs) to track pointer movements. It updates the position of a floating teal circle based on pointer events inside any of the target boxes.

import React, { createRef, useMemo, useState } from 'react';
import { animate, useValue, useMove } from 'react-ui-animate';

const App = () => {
  const [pos, setPos] = useValue({ x: 0, y: 0 });

  const refs = useMemo(
    () => Array.from({ length: 3 }, () => createRef<HTMLDivElement>()),
    []
  );

  useMove(refs, function ({ event }) {
    setPos({ x: event.clientX, y: event.clientY });
  });

  return (
    <>
      <animate.div
        style={{
          width: 50,
          height: 50,
          backgroundColor: 'teal',
          borderRadius: 4,
          translateX: pos.x,
          translateY: pos.y,
          position: 'fixed',
          top: 0,
          left: 0,
          pointerEvents: 'none',
        }}
      />

      {refs.map((r, i) => (
        <div
          key={i}
          ref={r}
          style={{
            width: 400,
            height: 60,
            backgroundColor: '#f1f1f1',
            borderRadius: 4,
            border: '1px solid #e1e1e1',
            marginBottom: 40,
          }}
        />
      ))}
    </>
  );
};

export default App;