Skip to content

Use Virtual Anchors

Sometimes there is no real DOM element that should act as the anchor. You may want to position a surface at the cursor, at a saved coordinate, or against a synthetic rectangle such as a text selection.

That is where virtual anchors come in.

This guide covers two practical paths:

When To Reach For A Virtual Anchor

Use a virtual anchor when:

  • The surface should appear at a pointer location
  • The anchor geometry is computed rather than tied to one element
  • You are building a context menu, inspection tool, or selection-based UI

Path 1: Follow The Pointer

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

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

const context = useFloatingContext({ refs: { anchorEl, floatingEl } });
const { styles } = usePosition(context, {
  placement: "right-start",
});

useClientPoint(context, {
  trackingAreaEl,
  trackingMode: "follow",
});

useHover(context);
</script>

In follow mode, pointer movement updates the virtual anchor and the floating surface keeps tracking while open.

Path 2: Keep The Opening Point Still

Sometimes you want the surface to open at the pointer location and stay there even if the pointer moves later.

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

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

const context = useFloatingContext({ refs: { anchorEl, floatingEl } });
const { styles } = usePosition(context, {
  placement: "bottom-start",
});

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

useEscapeKey(context);

function openMenu() {
  context.state.setOpen(true);
}
</script>

Static mode matters because it prevents the menu from drifting as the pointer moves after open. The coordinate is captured at the opening interaction and held steady.

Manual Virtual Anchors

If you already have coordinates or a computed rectangle, you do not need useClientPoint(). You can pass a manual virtual anchor to useFloatingContext().

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

const floatingEl = ref<HTMLElement | null>(null);
const open = ref(true);

const virtualAnchor = {
  getBoundingClientRect() {
    return {
      x: 160,
      y: 120,
      top: 120,
      left: 160,
      right: 160,
      bottom: 120,
      width: 0,
      height: 0,
      toJSON() {
        return this;
      },
    };
  },
};

const anchorEl = ref(virtualAnchor);

const context = useFloatingContext({ refs: { anchorEl, floatingEl }, state: { open } });
const { styles } = usePosition(context);
</script>

Where To Go Next

Use Virtual Anchors has loaded