Vize

mouse-in-element

Track the pointer position relative to an element.

Package @vizejs/composable/mouse-in-element
Own the source vize lib pull composable:mouse-in-element
Runtime exports useMouseInElement
Gzip budget 3072 B

Minimal setup

Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).

vp install @vizejs/composable

Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.

import { useMouseInElement } from "@vizejs/composable/mouse-in-element";

Try it

Compare native client and guide-relative coordinates

Read an element's current geometry on actual mouse moves and preserve its last inside location when the pointer leaves.

Open the interactive example

What to observe: Move inside the pad, shift it and move again. Relative coordinates use the new transformed rectangle. Leaving with handleOutside: false updates Outside but freezes the last inside coordinates. Stop relative readings, then move: the underlying client coordinates still change while relative state stays fixed.

useMouseInElement builds on the real client-coordinate mouse observer. Every relevant move reads getBoundingClientRect; transforms and scrolling are reflected on the next move, without an independent resize subscription. Its stop control freezes the relative watcher, not the underlying mouse subscription; the component scope disposes both. Zero-size rectangles count as outside. SSR has zero geometry and isOutside: true, with no measured browser position. The displayed coordinates describe this iframe, and the sample's native checkbox only changes its own CSS transform.

Compare the initial and interacted states
Compare native client and guide-relative coordinates: initial state
Initial state
Compare native client and guide-relative coordinates: state after the verified interactions
After the verified interactions

Copy the example

Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.

<script setup lang="ts">
import { ref, useTemplateRef } from "vue";
import { useMouseInElement } from "@vizejs/composable/mouse-in-element";

const pad = useTemplateRef<HTMLElement>("pad");
const shifted = ref(false);
const stopped = ref(false);
const { x, y, elementX, elementY, elementWidth, elementHeight, isOutside, stop } =
  useMouseInElement(pad, { handleOutside: false });

function stopRelativeReadings(): void {
  stop();
  stopped.value = true;
}
</script>

<template>
  <div class="composable-example">
    <label class="coordinate-toggle"
      ><input v-model="shifted" type="checkbox" /> Shift coordinate pad</label
    >
    <section ref="pad" class="coordinate-pad" :class="{ shifted }" aria-label="Coordinate pad">
      <strong>Locate the pointer inside a guide</strong>
      <p>Move here, then outside the border.</p>
    </section>
    <output aria-live="polite">
      Relative: {{ Math.round(elementX) }}, {{ Math.round(elementY) }} · Outside:
      {{ isOutside ? "yes" : "no" }} · {{ stopped ? "Stopped" : "Observing" }}
    </output>
    <p class="mouse-client">Client: {{ Math.round(x) }}, {{ Math.round(y) }} CSS pixels</p>
    <p class="mouse-size">
      Recorded pad: {{ Math.round(elementWidth) }} × {{ Math.round(elementHeight) }} CSS pixels
    </p>
    <button type="button" :disabled="stopped" @click="stopRelativeReadings">
      Stop relative readings
    </button>
    <p>
      Each mouse move reads the current native rectangle, including transforms and scrolling. Shift
      the pad and move again to compare the new local coordinates. With handleOutside: false,
      leaving updates Outside but retains the last inside coordinates.
    </p>
    <p>
      Stop freezes relative coordinates, size and Outside. The underlying client mouse coordinates
      still update until this component's scope is disposed. Server rendering starts with zero
      geometry and Outside: yes; it has not measured a browser element.
    </p>
  </div>
</template>

<style scoped>
.coordinate-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;

  input {
    inline-size: auto;
  }
}
.coordinate-pad {
  box-sizing: border-box;
  inline-size: 180px;
  max-inline-size: calc(100% - 24px);
  min-block-size: 120px;
  padding: 12px;
  border: 1px solid currentColor;
  border-radius: 8px;

  &.shifted {
    transform: translateX(24px);
  }
}
</style>

Runtime contract

Utility Category Stability SSR Hydration Cleanup Targets Host globals Uses
useMouseInElement input experimental deterministic-fallback stable caller, reactive-scope web, desktop window useMouse, resolveElement, tryOnScopeDispose

API

useMouseInElement

Track the pointer position relative to an element. Built on useMouse in client coordinates: every move re-reads the element rectangle, so transforms and scrolling are reflected. Server renders report zero geometry and isOutside: true.

function useMouseInElement( target: MaybeElementTarget, options: UseMouseInElementOptions = {}, ): MouseInElementControls

Types

UseMouseInElementOptions

Options for useMouseInElement.

Member Type Description
handleOutside? boolean Keep updating element-relative coordinates while the pointer is outside the element. When false, they freeze at the last inside position.

MouseInElementControls

Reactive state returned by useMouseInElement.

Member Type Description
x Readonly<Ref<number>> Latest horizontal coordinate.
y Readonly<Ref<number>> Latest vertical coordinate.
sourceType Readonly<Ref<MouseSourceType | null>> Device that produced the latest coordinates; null before any input.
elementX Readonly<Ref<number>> Pointer position relative to the element's left edge.
elementY Readonly<Ref<number>> Pointer position relative to the element's top edge.
elementPositionX Readonly<Ref<number>> Element left edge in viewport coordinates at the latest update.
elementPositionY Readonly<Ref<number>> Element top edge in viewport coordinates at the latest update.
elementWidth Readonly<Ref<number>> Element width at the latest update.
elementHeight Readonly<Ref<number>> Element height at the latest update.
isOutside Readonly<Ref<boolean>> Whether the pointer is outside the element. true during server rendering.
stop () => void Stop updating element-relative state. Idempotent.