553

React useMouse: cursor tracking without the re-render tax

Every coordinate in one call, a callback-first API that skips re-renders, and opt-in watch() for reactive state.

August 10, 2026

useMouse from reactuse tracks the cursor and returns every coordinate system you might need — page, viewport, and element-relative. Its key design choice: by default it re-renders your component zero times. You get a callback and an imperative snapshot; reactive state is one method call away when you want it.

Every coordinate, one call

import { useMouse } from '@siberiacancode/reactuse';
 
function CursorReadout() {
  // attach the ref to the element you want coordinates relative to
  const mouse = useMouse<HTMLDivElement>();
 
  return <div ref={mouse.ref}>{/* ... */}</div>;
}

The default is a callback — zero re-renders

By default useMouse puts nothing in React state. It keeps the latest value in a ref, exposes it as snapshot, and fires a callback on every move. The fast path is talking to the DOM directly and leaving React out of the loop:

import { useMouse } from '@siberiacancode/reactuse';
import { useRef } from 'react';
 
function SpotlightCard() {
  const spotlightRef = useRef<HTMLDivElement>(null);
 
  // fires on every move — but we never setState.
  // write straight to the DOM via CSS custom properties.
  const mouse = useMouse<HTMLDivElement>((value) => {
    const spotlight = spotlightRef.current;
    if (!spotlight) return;
    spotlight.style.setProperty('--x', `${value.elementX}px`);
    spotlight.style.setProperty('--y', `${value.elementY}px`);
  });
 
  return (
    <div ref={mouse.ref} style={{ position: 'relative' }}>
      <div
        ref={spotlightRef}
        style={{
          position: 'absolute',
          inset: 0,
          background:
            'radial-gradient(300px circle at var(--x) var(--y), rgba(255,255,255,0.1), transparent 65%)'
        }}
      />
    </div>
  );
}

The gradient tracks the cursor at 60fps and the component renders exactly once. For spotlights, custom cursors, canvas, and parallax, that's exactly what you want.

When you do want state: watch()

Need the coordinates in React — as text, a condition, or input to another component? Reactivity is opt-in via one method:

import { useMouse } from '@siberiacancode/reactuse';
 
function LiveCoords() {
  const mouse = useMouse<HTMLDivElement>();
  // opt in — now the component re-renders on move
  const position = mouse.watch();
 
  return (
    <p>
      x: {position.elementX}, y: {position.elementY}
    </p>
  );
}

The value lives in a ref; the hook only re-renders once watch() is called. Don't call it, don't pay for it. One hook, two modes — chosen per component by whether you read snapshot or call watch(). That's the part no other mouse hook does.

Ref or window — same hook

Pass nothing and you get a ref to attach (element-relative for free). Pass a target like window directly and it tracks that, returning just snapshot and watch:

// global, no ref to attach
const mouse = useMouse((value) => {
  console.log(value.clientX, value.clientY);
});

Takeaways

  • useMouse re-renders zero times by default — callback + snapshot, ideal for spotlights, cursors, canvas, parallax.
  • watch() is opt-in reactivity — call it only where you need coordinates in state.
  • One ref gives every coordinate system, scroll correction included; pass window for global tracking.