or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

index.mddocs/

@floating-ui/vue

@floating-ui/vue provides Vue.js integration for Floating UI, a positioning library for creating floating elements like tooltips, popovers, dropdowns, and menus. It offers Vue-specific composables and utilities built on top of @floating-ui/dom, enabling developers to easily create accessible floating UI components in Vue applications with automatic positioning, collision detection, and Vue-reactive state management.

Package Information

  • Package Name: @floating-ui/vue
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @floating-ui/vue

Core Imports

import { useFloating, arrow } from "@floating-ui/vue";

You can also import middleware directly:

import { useFloating, arrow, offset, flip, shift } from "@floating-ui/vue";

For CommonJS:

const { useFloating, arrow, offset, flip, shift } = require("@floating-ui/vue");

Basic Usage

<template>
  <div>
    <button ref="referenceRef" @click="show = !show">
      Toggle tooltip
    </button>
    <div
      v-if="show"
      ref="floatingRef"
      :style="floatingStyles"
      class="tooltip"
    >
      This is a tooltip
      <div ref="arrowRef" :style="arrowStyles" class="arrow"></div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';
import { useFloating, arrow, offset, flip, shift } from '@floating-ui/vue';

const show = ref(false);
const referenceRef = ref(null);
const floatingRef = ref(null);
const arrowRef = ref(null);

const { floatingStyles, placement, middlewareData } = useFloating(
  referenceRef,
  floatingRef,
  {
    open: show,
    placement: 'top',
    middleware: [
      offset(10),
      flip(),
      shift({ padding: 5 }),
      arrow({
        element: arrowRef,
        padding: 5,
      }),
    ],
  }
);

const arrowStyles = computed(() => {
  if (!middlewareData.value.arrow) return {};
  
  const { x, y } = middlewareData.value.arrow;
  return {
    left: x != null ? `${x}px` : '',
    top: y != null ? `${y}px` : '',
  };
});
</script>

Architecture

@floating-ui/vue is built around several key architectural components that work together to provide Vue-reactive floating element positioning:

  • Vue Composable Layer: The useFloating composable serves as the primary interface, providing Vue-reactive state management for positioning data and automatic cleanup through Vue's lifecycle system.
  • Template Ref Integration: Seamlessly integrates with Vue's template ref system, accepting both element refs and component instance refs while handling the unwrapping automatically.
  • Reactive Configuration: All configuration options (placement, middleware, open state) accept Vue refs, getters, or static values, enabling fully reactive positioning updates when dependencies change.
  • Middleware Pipeline: Leverages @floating-ui/dom's middleware system for extensible positioning modifications, with Vue-specific wrappers (like the arrow function) that handle Vue ref unwrapping.
  • Automatic Updates: Provides intelligent position updates through watchers that respond to configuration changes and element mounting/unmounting, with optional manual update control.
  • Type Safety: Full TypeScript integration with Vue-specific type wrappers (MaybeReadonlyRef, MaybeReadonlyRefOrGetter) that preserve type safety while accommodating Vue's reactivity patterns.

Capabilities

useFloating Composable

Main composable for positioning floating elements relative to reference elements with Vue reactivity.

/**
 * Computes the x and y coordinates that will place the floating element next to a reference element
 * @param reference - The reference template ref
 * @param floating - The floating template ref  
 * @param options - The floating options
 * @returns Object with reactive positioning values and utilities
 */
function useFloating<T extends ReferenceElement = ReferenceElement>(
  reference: Readonly<Ref<MaybeElement<T>>>,
  floating: Readonly<Ref<MaybeElement<FloatingElement>>>,
  options?: UseFloatingOptions<T>
): UseFloatingReturn;

interface UseFloatingOptions<T extends ReferenceElement = ReferenceElement> {
  /** Represents the open/close state of the floating element. @default true */
  open?: MaybeReadonlyRefOrGetter<boolean | undefined>;
  /** Where to place the floating element relative to its reference element. @default 'bottom' */
  placement?: MaybeReadonlyRefOrGetter<Placement | undefined>;
  /** The type of CSS position property to use. @default 'absolute' */
  strategy?: MaybeReadonlyRefOrGetter<Strategy | undefined>;
  /** These are plain objects that modify the positioning coordinates in some fashion, or provide useful data for the consumer to use. @default undefined */
  middleware?: MaybeReadonlyRefOrGetter<Middleware[] | undefined>;
  /** Whether to use transform instead of top and left styles to position the floating element (floatingStyles). @default true */
  transform?: MaybeReadonlyRefOrGetter<boolean | undefined>;
  /** Callback to handle mounting/unmounting of the elements. @default undefined */
  whileElementsMounted?: (
    reference: T,
    floating: FloatingElement,
    update: () => void,
  ) => () => void;
}

interface UseFloatingReturn {
  /** The x-coord of the floating element */
  x: Readonly<Ref<number>>;
  /** The y-coord of the floating element */
  y: Readonly<Ref<number>>;
  /** The stateful placement, which can be different from the initial placement passed as options */
  placement: Readonly<Ref<Placement>>;
  /** The type of CSS position property to use */
  strategy: Readonly<Ref<Strategy>>;
  /** Additional data from middleware */
  middlewareData: Readonly<Ref<MiddlewareData>>;
  /** The boolean that let you know if the floating element has been positioned */
  isPositioned: Readonly<Ref<boolean>>;
  /** CSS styles to apply to the floating element to position it */
  floatingStyles: Readonly<Ref<{
    position: Strategy;
    top: string;
    left: string;
    transform?: string;
    willChange?: string;
  }>>;
  /** The function to update floating position manually */
  update: () => void;
}

Arrow Middleware

Positions an arrow element centered relative to the reference element.

/**
 * Positions an inner element of the floating element such that it is centered to the reference element
 * @param options - The arrow options
 * @returns Middleware for arrow positioning
 */
function arrow(options: ArrowOptions): Middleware;

interface ArrowOptions {
  /** The arrow element or template ref to be positioned. @required */
  element: MaybeReadonlyRefOrGetter<MaybeElement<Element>>;
  /** The padding between the arrow element and the floating element edges. Useful when the floating element has rounded corners. @default 0 */
  padding?: Padding;
}

Re-exported Middleware from @floating-ui/dom

The following middleware functions are re-exported from @floating-ui/dom for convenience:

/** Chooses the placement that has the most space available automatically */
function autoPlacement(options?: AutoPlacementOptions): Middleware;

/** Changes the placement of the floating element in order to keep it in view when the preferred placement does not fit */
function flip(options?: FlipOptions): Middleware;

/** Shifts the floating element in order to keep it in view when it will overflow the clipping boundary */
function shift(options?: ShiftOptions): Middleware;

/** Provides data to position an inner element of the floating element so that it appears centered to the reference element */
function offset(options?: OffsetOptions): Middleware;

/** Provides data that allows you to change the size of the floating element */
function size(options?: SizeOptions): Middleware;

/** Provides data to hide the floating element in applicable situations */
function hide(options?: HideOptions): Middleware;

/** Provides improved positioning for inline reference elements that can span over multiple lines */
function inline(options?: InlineOptions): Middleware;

/** Built-in limitShift() limiter that will stop shift() at a certain point */
function limitShift(options?: {
  /** Offset when limiting starts. @default 0 */
  offset?: number | OffsetOptions;
  /** Axis that is limited. @default 'all' */
  mainAxis?: boolean;
  /** Cross axis that is limited. @default 'all' */
  crossAxis?: boolean;
}): {
  fn: (state: MiddlewareState) => Coords;
};

Utility Functions

/** Computes the `x` and `y` coordinates that will place the floating element next to a reference element */
function computePosition(
  reference: ReferenceElement,
  floating: FloatingElement,
  options?: ComputePositionConfig
): Promise<ComputePositionReturn>;

/** Automatically updates the position of the floating element when necessary */
function autoUpdate(
  reference: ReferenceElement,
  floating: FloatingElement,
  update: () => void,
  options?: AutoUpdateOptions
): () => void;

/** Resolves with an object of overflow side offsets that determine how much the element is overflowing a given clipping boundary */
function detectOverflow(
  state: MiddlewareState,
  options?: DetectOverflowOptions
): Promise<SideObject>;

/** Returns the overflow ancestors that can potentially clip the floating element */
function getOverflowAncestors(
  element: Element,
  list?: (Element | Window)[]
): (Element | Window)[];

/** This is useful for packages that create a \"platform\" */
const platform: Platform;

Types

Core Vue-specific Types

/** Represents a value that can be a readonly ref */
type MaybeReadonlyRef<T> = T | Readonly<Ref<T>>;

/** Represents a value, readonly ref, or getter function */
type MaybeReadonlyRefOrGetter<T> = MaybeReadonlyRef<T> | (() => T);

/** Represents a DOM element, Vue component instance, or nullish value */
type MaybeElement<T> = T | ComponentPublicInstance | null | undefined;

Re-exported Types from @floating-ui/dom

The package re-exports all types from @floating-ui/dom including:

/** Placement options for positioning */
type Placement = 
  | 'top'
  | 'top-start'
  | 'top-end'
  | 'right'
  | 'right-start'
  | 'right-end'
  | 'bottom'
  | 'bottom-start'
  | 'bottom-end'
  | 'left'
  | 'left-start'
  | 'left-end';

/** CSS positioning strategy */
type Strategy = 'absolute' | 'fixed';

/** Reference element types */
type ReferenceElement = Element | VirtualElement;

/** Floating element type */
type FloatingElement = HTMLElement;

/** Virtual reference element for custom positioning */
interface VirtualElement {
  getBoundingClientRect(): ClientRectObject;
  contextElement?: Element;
}

/** Middleware function for positioning modifications */
interface Middleware {
  name: string;
  options?: any;
  fn: (state: MiddlewareState) => Promise<MiddlewareReturn> | MiddlewareReturn;
}

/** Data returned by middleware */
interface MiddlewareData {
  [key: string]: any;
}

/** Padding configuration for middleware */
type Padding = number | Partial<{
  top: number;
  right: number;
  bottom: number;
  left: number;
}>;

/** Coordinates interface */
interface Coords {
  x: number;
  y: number;
}

/** Rectangle dimensions */
interface Rect extends Coords {
  width: number;
  height: number;
}

/** Client rectangle object */
interface ClientRectObject extends Rect {
  top: number;
  left: number;
  right: number;
  bottom: number;
}

Usage Examples

Basic Tooltip

<template>
  <div>
    <button ref="reference" @click="open = !open">
      Click me
    </button>
    <div
      v-if="open"
      ref="floating"
      :style="floatingStyles"
      class="tooltip"
    >
      Tooltip content
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useFloating, offset } from '@floating-ui/vue';

const open = ref(false);
const reference = ref(null);
const floating = ref(null);

const { floatingStyles } = useFloating(reference, floating, {
  open,
  placement: 'top',
  middleware: [offset(10)],
});
</script>

Dropdown with Auto-placement

<template>
  <div>
    <button ref="reference" @click="open = !open">
      Menu
    </button>
    <div
      v-if="open"
      ref="floating"
      :style="floatingStyles"
      class="dropdown"
    >
      <div>Item 1</div>
      <div>Item 2</div>
      <div>Item 3</div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useFloating, autoPlacement, offset } from '@floating-ui/vue';

const open = ref(false);
const reference = ref(null);
const floating = ref(null);

const { floatingStyles } = useFloating(reference, floating, {
  open,
  middleware: [
    offset(5),
    autoPlacement(),
  ],
});
</script>

Using with Component References

<template>
  <div>
    <MyButton ref="reference" @click="open = !open">
      Click me
    </MyButton>
    <MyTooltip
      v-if="open"
      ref="floating"
      :style="floatingStyles"
    >
      Tooltip content
    </MyTooltip>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useFloating, offset } from '@floating-ui/vue';

const open = ref(false);
const reference = ref(null);
const floating = ref(null);

const { floatingStyles } = useFloating(reference, floating, {
  open,
  placement: 'bottom',
  middleware: [offset(10)],
});
</script>

Virtual Reference Element

<template>
  <div @contextmenu="showContextMenu">
    Right-click anywhere
    <div
      v-if="open"
      ref="floating"
      :style="floatingStyles"
      class="context-menu"
    >
      <div>Cut</div>
      <div>Copy</div>
      <div>Paste</div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useFloating } from '@floating-ui/vue';

const open = ref(false);
const reference = ref(null);
const floating = ref(null);

const { floatingStyles } = useFloating(reference, floating, {
  open,
  placement: 'right-start',
});

function showContextMenu(event) {
  event.preventDefault();
  
  // Create virtual reference element at cursor position
  reference.value = {
    getBoundingClientRect() {
      return {
        x: event.clientX,
        y: event.clientY,
        top: event.clientY,
        left: event.clientX,
        right: event.clientX,
        bottom: event.clientY,
        width: 0,
        height: 0,
      };
    },
  };
  
  open.value = true;
}
</script>