Positioning

useClientPoint

Positions a floating element relative to pointer coordinates using a virtual anchor.

useClientPoint positions a floating element relative to cursor coordinates. It replaces the node's anchor reference with a virtual element that tracks pointer movements or captures trigger click coordinates.

Use it for cursor-following tooltips, image hover previews, and right-click context menus.

Type

function useClientPoint(node: FloatingNode, options?: UseClientPointOptions): UseClientPointReturn;

type TrackingMode = "follow" | "static";

interface UseClientPointOptions {
  /**
   * Element that receives pointer listeners and provides fallback geometry
   * for the virtual anchor. Defaults to `document.documentElement`.
   */
  trackingAreaEl?: Ref<HTMLElement | null>;

  /**
   * Enables or disables client-point behavior without removing the composable.
   * @default true
   */
  enabled?: MaybeRefOrGetter<boolean>;

  /**
   * Optional externally controlled x coordinate. Both `x` and `y` must resolve
   * to non-null numbers for controlled mode; a single axis has no effect.
   */
  x?: MaybeRefOrGetter<number | null>;

  /**
   * Optional externally controlled y coordinate. Both `x` and `y` must resolve
   * to non-null numbers for controlled mode; a single axis has no effect.
   */
  y?: MaybeRefOrGetter<number | null>;

  /**
   * Chooses how the pointer position behaves after the floating element opens.
   * Not reactive after setup.
   * @default "follow"
   */
  trackingMode?: TrackingMode;
}

interface UseClientPointReturn {
  coordinates: Readonly<Ref<{ x: number | null; y: number | null }>>;
}

Options

NameTypeDefaultNotes
trackingAreaElRef<HTMLElement | null>document.documentElementTarget element that receives pointer listeners.
enabledMaybeRefOrGetter<boolean>trueReactive toggle. Disabling disconnects listeners and restores static anchoring.
x / yMaybeRefOrGetter<number | null>nullExternal coordinates. When both are non-null finite numbers, enters controlled mode.
trackingMode"follow" | "static""follow""follow" updates coordinates continuously while open. "static" captures coordinates at open time.

Returns

NameTypeNotes
coordinatesReadonly<Ref<{ x: number | null; y: number | null }>>Last recorded pointer coordinates. null before first trigger and when closed in uncontrolled mode.

Details

Virtual Element Replacement

useClientPoint replaces node.refs.anchorEl.value with a virtual element implementing getBoundingClientRect(). The virtual element returns a 0×0 rectangle positioned at the active pointer coordinates.

Tracking Modes

  • trackingMode: "follow": The floating panel continuously tracks the pointer while open. Only mouse and pen input moves the surface; touch interactions are ignored to prevent layout jumping.
  • trackingMode: "static": The floating panel captures the opening event's position (e.g. right-click coordinate) and anchors there. Moving the pointer afterward does not move the panel. Closing clears the point.

Controlled Coordinates

When you supply both x and y as finite numbers, useClientPoint detaches internal pointer listeners and drives the virtual element directly from your coordinates. This is useful when pointer coordinates are managed by external canvas or map components.

Example

Context Menu (Static Tracking)

Right-click anywhere in an area to open a context menu anchored at the click point:

<script setup lang="ts">
import { ref } from "vue";
import {
  useClientPoint,
  useEscapeKey,
  useFloatingNode,
  useOutsideClick,
  usePosition,
} from "v-float";

const trackingAreaEl = ref<HTMLElement | null>(null);
const anchorEl = ref<HTMLElement | null>(null);
const floatingEl = ref<HTMLElement | null>(null);

const node = useFloatingNode({ anchorEl, floatingEl });
usePosition(node, {
  placement: "bottom-start",
  middlewares: {
    flip: true,
    shift: { padding: 8 },
  },
});

useClientPoint(node, {
  trackingAreaEl,
  trackingMode: "static",
});

useOutsideClick(node);
useEscapeKey(node);

function onContextMenu(event: MouseEvent) {
  event.preventDefault();
  node.open.value = true;
}
</script>

<template>
  <div ref="trackingAreaEl" class="canvas-area" @contextmenu="onContextMenu">
    Right-click inside this container

    <div v-if="node.open.value" ref="floatingEl" class="context-menu">
      <button @click="node.open.value = false">Cut</button>
      <button @click="node.open.value = false">Copy</button>
      <button @click="node.open.value = false">Paste</button>
    </div>
  </div>
</template>

<style>
.canvas-area {
  height: 240px;
  border: 1px dashed #ccc;
  display: flex;
  align-items: center;
  justify-content: center;
}
.context-menu {
  display: flex;
  flex-direction: column;
  background: white;
  border: 1px solid #ccc;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  padding: 4px;
}
</style>

See Also

Released under the MIT License. Copyright © 2026