Positioning

usePosition

Add reactive positioning styles to a floating node using Floating UI.

usePosition computes reactive screen coordinates and inline styles for an existing floating node. It configures the middleware pipeline, sets placement and positioning strategy, auto-updates on scroll and resize, and automatically synchronizes positioning styles to the floating DOM element.

Type

function usePosition(node: FloatingNode, options?: UsePositionOptions): FloatingPosition;

interface UsePositionOptions {
  placement?: MaybeRefOrGetter<Placement | undefined>;
  strategy?: MaybeRefOrGetter<Strategy | undefined>;
  transform?: MaybeRefOrGetter<boolean | undefined>;
  middlewares?: MaybeRefOrGetter<UsePositionMiddlewaresOptions | Middleware[] | undefined>;
  autoUpdate?: MaybeRefOrGetter<boolean | AutoUpdateOptions | undefined>;
  enabled?: MaybeRefOrGetter<boolean>;
  applyStyles?: MaybeRef<boolean | undefined> | ApplyStylesFn;
}

type ApplyStylesFn = (element: HTMLElement, styles: FloatingStyles) => void | (() => void);

interface UsePositionMiddlewaresOptions {
  inline?: true | false | InlineOptions;
  offset?: true | false | OffsetOptions;
  flip?: true | false | FlipOptions;
  autoPlacement?: true | false | AutoPlacementOptions;
  shift?: true | false | ShiftOptions;
  matchWidth?: boolean;
  size?: SizeOptions;
  hide?: true | false | HideOptions;
  arrow?: true | false | UsePositionArrowOptions;
  custom?: MaybeRefOrGetter<Middleware[] | undefined>;
}

interface UsePositionArrowOptions {
  element?: Ref<HTMLElement | null>;
  padding?: Padding;
}

interface FloatingPosition {
  x: Readonly<Ref<number>>;
  y: Readonly<Ref<number>>;
  strategy: Readonly<Ref<Strategy>>;
  placement: Readonly<Ref<Placement>>;
  middlewareData: Readonly<Ref<MiddlewareData>>;
  isPositioned: Readonly<Ref<boolean>>;
  styles: Readonly<Ref<FloatingStyles>>;
  update: () => Promise<void>;
}

type FloatingStyles = {
  position: Strategy;
  top: string;
  left: string;
  transform?: string;
  "will-change"?: string;
} & {
  [key: `--${string}`]: any;
};

Options

NameTypeDefaultNotes
placementMaybeRefOrGetter<Placement>"bottom"Desired side and alignment (e.g. "bottom-start"). Reactive.
strategyMaybeRefOrGetter<Strategy>"absolute"CSS positioning strategy: "absolute" or "fixed". Reactive.
transformMaybeRefOrGetter<boolean>truetrue uses CSS transform: translate(x, y). false sets top and left.
middlewaresMaybeRefOrGetter<UsePositionMiddlewaresOptions | Middleware[]>{}Declarative middleware configuration or a raw Middleware[] array.
autoUpdateMaybeRefOrGetter<boolean | AutoUpdateOptions>trueAutomatically recomputes on resize, scroll, and layout changes. Set false to disable.
enabledMaybeRefOrGetter<boolean>trueControls whether positioning computations and viewport listeners are active.
applyStylesMaybeRef<boolean> | ApplyStylesFntrueAutomatically synchronizes computed styles to node.refs.floatingEl. Pass false to disable or a custom function to override.

Declarative Middleware Options

When passing an object to middlewares, VFloat resolves built-in middlewares in the recommended pipeline order:

OptionTypePipeline StepPurpose
inlineboolean | InlineOptions1Dissects multi-line inline triggers into individual client rects.
offsetnumber | OffsetOptions2Adds distance between the anchor and the floating panel.
flipboolean | FlipOptions3Flips to alternate placements when space is constrained.
autoPlacementboolean | AutoPlacementOptions4Chooses the placement with the most available space (mutually exclusive with flip).
shiftboolean | ShiftOptions5Nudges the element along the viewport boundary to stay visible.
matchWidthboolean6Sets panel width to match the anchor's measured width via size.
sizeSizeOptions7Measures boundary limits and invokes a custom resizing function.
hideboolean | HideOptions8Flags whether the anchor is clipped or the panel escaped boundaries.
arrowboolean | UsePositionArrowOptions9Calculates offsets for an arrow element using node.refs.arrowEl.
customMiddleware[]10Appends custom Floating UI middleware instances at the end of the pipeline.

Alternatively, you can pass a raw Middleware[] array to middlewares to fully customize the middleware instances and execution order.

Returns

NameTypeNotes
x / yReadonly<Ref<number>>Computed horizontal and vertical coordinates in pixels.
strategyReadonly<Ref<Strategy>>Active positioning strategy ("absolute" or "fixed").
placementReadonly<Ref<Placement>>Effective placement after middleware execution (e.g. after flipping).
middlewareDataReadonly<Ref<MiddlewareData>>Raw output produced by middlewares (such as arrow coordinates or hide flags).
isPositionedReadonly<Ref<boolean>>Becomes true once coordinates are computed for mounted elements. Resets to false on unmount.
stylesReadonly<Ref<FloatingStyles>>Reactive inline style object with device-pixel-ratio subpixel rounding. Synchronized directly to node.refs.floatingEl by default (applyStyles: true), or manually bindable via :style="styles" when applyStyles: false.
update() => Promise<void>Imperatively forces an immediate coordinate recomputation.

Details

Subpixel Snapping and Performance

styles applies Math.round(val * dpr) / dpr to coordinate outputs using window.devicePixelRatio. This prevents blurred text and rendering artifacts on high-DPI displays. On displays with DPR ≥ 1.5, will-change: transform is automatically applied.

Server-Side Rendering (SSR)

usePosition is safe to run in SSR environments. On the server, styles renders safe baseline rules:

position: absolute;
left: 0;
top: 0;

Full coordinate calculation begins once components mount in the browser.

Automatic Style Binding & Overrides

By default (applyStyles: true), usePosition automatically synchronizes positioning styles (position, left, top, transform, will-change, and custom CSS variables) directly to node.refs.floatingEl.value.style. You do not need to bind :style="styles" in your template.

If you need full control over style application, you can:

  1. Opt out of automatic binding by setting applyStyles: false. styles remains reactive and available on the return object to bind via :style="styles".
  2. Provide a custom applicator callback:
    usePosition(node, {
      applyStyles(el, styles) {
        Object.assign(el.style, styles);
        // Custom style application or animation logic
        return () => {
          // Optional cleanup callback
        };
      },
    });
    

Dynamic Middleware Contributions

Companion composables such as useArrow dynamically register their middleware into the node's internal registry. You do not need to configure arrow middleware manually when calling useArrow(node).

Example

Declarative Middleware Configuration (Automatic Styling)

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

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

const node = useFloatingNode({ anchorEl, floatingEl });
const { placement } = usePosition(node, {
  placement: "top",
  middlewares: {
    offset: 8,
    flip: true,
    shift: { padding: 8 },
  },
  enabled: () => node.open.value,
});

useHover(node);
</script>

<template>
  <button ref="anchorEl">Hover me</button>
  <div v-if="node.open" ref="floatingEl" :data-placement="placement">Tooltip content</div>
</template>

Match Anchor Width

For dropdowns and select menus where the panel should match the trigger width:

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

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

const node = useFloatingNode({ anchorEl, floatingEl });
usePosition(node, {
  placement: "bottom-start",
  middlewares: {
    offset: 4,
    matchWidth: true,
  },
});

useClick(node);
</script>

<template>
  <button ref="anchorEl" style="width: 240px">Select an option</button>
  <div v-if="node.open" ref="floatingEl">Matches anchor width (240px)</div>
</template>

Manual Style Binding (applyStyles: false)

If you want to manage style bindings manually in your template:

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

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

const node = useFloatingNode({ anchorEl, floatingEl });
const { styles } = usePosition(node, {
  placement: "bottom",
  applyStyles: false,
});
</script>

<template>
  <button ref="anchorEl">Trigger</button>
  <div v-if="node.open" ref="floatingEl" :style="styles">Manual style binding</div>
</template>

See Also

Released under the MIT License. Copyright © 2026