Skip to content

Vue

This package provides Vue bindings for @floating-ui/dom — a library that provides anchor positioning for a floating element to position it next to a given reference element.

Install

npm install @floating-ui/vue
npm install @floating-ui/vue

Usage

useFloating()useFloating() is the main composable:

<script setup>
import {ref} from 'vue';
import {useFloating} from '@floating-ui/vue';
 
const reference = ref(null);
const floating = ref(null);
const {x, y, strategy} = useFloating(reference, floating);
</script>
 
<template>
  <button ref="reference">Button</button>
  <div
    :style="{
      position: strategy,
      top: `${y ?? 0}px`,
      left: `${x ?? 0}px`,
      width: 'max-content',
    }"
    ref="floating"
  >
    Tooltip
  </div>
</template>
<script setup>
import {ref} from 'vue';
import {useFloating} from '@floating-ui/vue';
 
const reference = ref(null);
const floating = ref(null);
const {x, y, strategy} = useFloating(reference, floating);
</script>
 
<template>
  <button ref="reference">Button</button>
  <div
    :style="{
      position: strategy,
      top: `${y ?? 0}px`,
      left: `${x ?? 0}px`,
      width: 'max-content',
    }"
    ref="floating"
  >
    Tooltip
  </div>
</template>

This will position the floating Tooltip element at the bottom center of the Button element by default.

  • xx and yy are refs with the positioning coordinates. These ref values are nullnull initially.
  • strategystrategy is a ref with a string value, 'absolute''absolute' (default) or 'fixed''fixed'.

Return value

The composable returns all the values from computePosition, plus some extras to work with Vue. This includes data about the final placement and middleware data which are useful when rendering.

Options

The composable accepts all the options from computePosition, which allows you to customize the position. Here’s an example:

import {
  useFloating,
  offset,
  flip,
  shift,
} from '@floating-ui/vue';
 
// Inside your component
useFloating(reference, floating, {
  placement: 'right',
  middleware: [offset(10), flip(), shift()],
});
import {
  useFloating,
  offset,
  flip,
  shift,
} from '@floating-ui/vue';
 
// Inside your component
useFloating(reference, floating, {
  placement: 'right',
  middleware: [offset(10), flip(), shift()],
});

The composable also accepts Ref options:

import {
  useFloating,
  offset,
  flip,
  shift,
} from '@floating-ui/vue';
 
// Inside your component
const placement = ref('right');
const middleware = ref([offset(10), flip(), shift()]);
useFloating(reference, floating, {
  placement,
  middleware,
});
import {
  useFloating,
  offset,
  flip,
  shift,
} from '@floating-ui/vue';
 
// Inside your component
const placement = ref('right');
const middleware = ref([offset(10), flip(), shift()]);
useFloating(reference, floating, {
  placement,
  middleware,
});

Middleware can alter the positioning from the basic placementplacement, act as visibility optimizers, or provide data to use.

The docs for the middleware that were passed are available here:

Updating

The position is only calculated once on render, or when the referencereference or floatingfloating elements changed — for example, the floating element get mounted via conditional rendering.

To ensure the floating element remains anchored to its reference element in a variety of scenarios without detaching — such as when scrolling or resizing the page — you can pass the autoUpdate utility to the whileElementsMountedwhileElementsMounted prop:

import {useFloating, autoUpdate} from '@floating-ui/vue';
 
// Inside your component
useFloating(reference, floating, {
  whileElementsMounted: autoUpdate,
});
import {useFloating, autoUpdate} from '@floating-ui/vue';
 
// Inside your component
useFloating(reference, floating, {
  whileElementsMounted: autoUpdate,
});

To pass options to autoUpdate:

useFloating(reference, floating, {
  whileElementsMounted(...args) {
    const cleanup = autoUpdate(...args, {animationFrame: true});
    // Important! Always return the cleanup function.
    return cleanup;
  },
});
useFloating(reference, floating, {
  whileElementsMounted(...args) {
    const cleanup = autoUpdate(...args, {animationFrame: true});
    // Important! Always return the cleanup function.
    return cleanup;
  },
});

Manual updating

While autoUpdate covers most cases where the position of the floating element must be updated, it does not cover every single one possible due to performance/platform limitations.

The composable returns an update()update() function to update the position at will:

<script setup>
const {update} = useFloating(reference, floating);
</script>
 
<template>
  <Panel @resize="update" />
</template>
<script setup>
const {update} = useFloating(reference, floating);
</script>
 
<template>
  <Panel @resize="update" />
</template>

Custom components

The composable also accepts custom component template refs:

<script setup>
import {ref} from 'vue';
import {useFloating} from '@floating-ui/vue';
 
import MyButton from './MyButton.vue';
import MyTooltip from './MyTooltip.vue';
 
const reference = ref(null);
const floating = ref(null);
const {x, y, strategy} = useFloating(reference, floating);
</script>
 
<template>
  <MyButton ref="reference">Button</MyButton>
  <MyTooltip ref="floating">Tooltip</MyTooltip>
</template>
<script setup>
import {ref} from 'vue';
import {useFloating} from '@floating-ui/vue';
 
import MyButton from './MyButton.vue';
import MyTooltip from './MyTooltip.vue';
 
const reference = ref(null);
const floating = ref(null);
const {x, y, strategy} = useFloating(reference, floating);
</script>
 
<template>
  <MyButton ref="reference">Button</MyButton>
  <MyTooltip ref="floating">Tooltip</MyTooltip>
</template>

Effects

Positioning is done in an async function, which means the position is ready during a microtask, after layout effects are executed. This means initially, the floating element is situated at the top-left (0, 0) of its offset container — so calling DOM methods that cause side-effects like scrolling will result in unexpected behavior.

The composable returns an isPositionedisPositioned boolean ref that lets you know if the floating element has been positioned:

const open = ref(false);
const {isPositioned} = useFloating(reference, floating, {
  // Synchronize `isPositioned` with an `open` ref.
  open,
});
 
// Each time the floating element opens, we want to focus and
// scroll some element into view.
watch(isPositioned, (isPositioned) => {
  if (isPositioned) {
    someElement.focus();
    someElement.scrollIntoView();
  }
});
const open = ref(false);
const {isPositioned} = useFloating(reference, floating, {
  // Synchronize `isPositioned` with an `open` ref.
  open,
});
 
// Each time the floating element opens, we want to focus and
// scroll some element into view.
watch(isPositioned, (isPositioned) => {
  if (isPositioned) {
    someElement.focus();
    someElement.scrollIntoView();
  }
});

The open option accepts a boolean ref that represents the open/close state of the floating element. This ensures you can wait each time it opens when the host component does not unmount, which is necessary in cases where the reference element relocates on the page.

Arrow

The arrow module exported from this package allows template refs in addition to elements:

import {arrow} from '@floating-ui/vue';
 
// Inside your component
const floatingArrow = ref(null);
useFloating({
  middleware: [arrow({element: floatingArrow})],
});
import {arrow} from '@floating-ui/vue';
 
// Inside your component
const floatingArrow = ref(null);
useFloating({
  middleware: [arrow({element: floatingArrow})],
});

Virtual Element

See Virtual Elements for details. Example:

<script setup>
import {onMounted, ref} from 'vue';
import {useFloating} from '@floating-ui/vue';
 
const reference = ref(null);
const floating = ref(null);
const {x, y, strategy} = useFloating(reference, floating);
 
onMounted(() => {
  /**
   * Assign the virtual element to reference inside
   * a lifecycle hook or effect or event handler.
   */
  reference.value = {
    getBoundingClientRect() {
      return {
        // ...
      };
    },
  };
});
</script>
 
<template>
  <div ref="floating">Tooltip</div>
</template>
<script setup>
import {onMounted, ref} from 'vue';
import {useFloating} from '@floating-ui/vue';
 
const reference = ref(null);
const floating = ref(null);
const {x, y, strategy} = useFloating(reference, floating);
 
onMounted(() => {
  /**
   * Assign the virtual element to reference inside
   * a lifecycle hook or effect or event handler.
   */
  reference.value = {
    getBoundingClientRect() {
      return {
        // ...
      };
    },
  };
});
</script>
 
<template>
  <div ref="floating">Tooltip</div>
</template>