> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/ueberdosis/tiptap/llms.txt
> Use this file to discover all available pages before exploring further.

# FloatingMenu

> Vue component that displays a menu on empty lines in the editor.

## Overview

The `FloatingMenu` component renders a menu that appears when the cursor is on an empty line. It's commonly used to provide quick access to block-level formatting options like headings, images, or other content types.

## Signature

```typescript theme={null}
const FloatingMenu: DefineComponent<{
  editor: Editor
  pluginKey?: string | PluginKey
  updateDelay?: number
  resizeDelay?: number
  options?: TippyOptions
  appendTo?: Element | (() => Element) | 'parent'
  shouldShow?: (props: {
    editor: Editor
    view: EditorView
    state: EditorState
    oldState?: EditorState
  }) => boolean
}>
```

## Props

<ParamField path="editor" type="Editor" required>
  The Tiptap editor instance. This prop is required.
</ParamField>

<ParamField path="pluginKey" type="string | PluginKey" default="'floatingMenu'">
  A unique key for the ProseMirror plugin. Use this if you need multiple floating menus or want to identify this specific plugin.
</ParamField>

<ParamField path="updateDelay" type="number" default="undefined">
  Delay in milliseconds before updating the menu position after cursor movement. Useful for debouncing rapid cursor changes.
</ParamField>

<ParamField path="resizeDelay" type="number" default="undefined">
  Delay in milliseconds before updating the menu position after a window resize event.
</ParamField>

<ParamField path="options" type="TippyOptions" default="{}">
  Configuration options for the underlying Tippy.js positioning library. Use this to customize the menu's placement, offset, animation, and other display properties.

  Common options:

  * `placement` - Position relative to reference (e.g., 'top', 'bottom', 'left', 'right')
  * `offset` - Distance from the reference element
  * `duration` - Animation duration
  * `zIndex` - CSS z-index value
</ParamField>

<ParamField path="appendTo" type="Element | (() => Element) | 'parent'" default="undefined">
  The element to append the floating menu to. Can be:

  * A DOM element
  * A function that returns a DOM element
  * The string `'parent'` to append to the editor's parent element

  By default, the menu is appended to `document.body`.
</ParamField>

<ParamField path="shouldShow" type="function" default="null">
  A callback function that determines whether the floating menu should be visible. Receives an object with:

  * `editor` - The editor instance
  * `view` - The ProseMirror EditorView
  * `state` - Current editor state
  * `oldState` - Previous editor state

  Return `true` to show the menu, `false` to hide it.

  By default, the menu shows when the cursor is in an empty paragraph and hides otherwise.
</ParamField>

## Lifecycle

* **onMounted**: Registers the floating menu plugin with the editor and sets up positioning
* **onBeforeUnmount**: Unregisters the plugin from the editor

## Styling

The component inherits attributes (`inheritAttrs: false` is set, but attributes are manually applied), allowing you to add custom classes, styles, or other HTML attributes:

```vue theme={null}
<FloatingMenu :editor="editor" class="custom-floating-menu" style="background: white;">
  <!-- content -->
</FloatingMenu>
```

## Examples

### Basic Usage

```vue theme={null}
<script setup>
import { useEditor, EditorContent, FloatingMenu } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

const editor = useEditor({
  content: '<p>Click in this empty paragraph below to see the floating menu</p><p></p>',
  extensions: [
    StarterKit,
  ],
})
</script>

<template>
  <div>
    <EditorContent :editor="editor" />
    <FloatingMenu :editor="editor" v-if="editor">
      <button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()">
        H1
      </button>
      <button @click="editor.chain().focus().toggleHeading({ level: 2 }).run()">
        H2
      </button>
      <button @click="editor.chain().focus().toggleBulletList().run()">
        Bullet List
      </button>
    </FloatingMenu>
  </div>
</template>
```

### Styled Floating Menu

```vue theme={null}
<script setup>
import { useEditor, EditorContent, FloatingMenu } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

const editor = useEditor({
  content: '<p>Start typing on a new line...</p>',
  extensions: [StarterKit],
})
</script>

<template>
  <div>
    <EditorContent :editor="editor" />
    <FloatingMenu :editor="editor" v-if="editor" class="floating-menu">
      <button 
        @click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
      >
        H1
      </button>
      <button 
        @click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
      >
        H2
      </button>
      <button 
        @click="editor.chain().focus().toggleBulletList().run()"
        :class="{ 'is-active': editor.isActive('bulletList') }"
      >
        Bullet List
      </button>
    </FloatingMenu>
  </div>
</template>

<style scoped>
.floating-menu {
  display: flex;
  gap: 0.25rem;
  padding: 0.25rem;
  background-color: white;
  border: 1px solid #ccc;
  border-radius: 0.5rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.floating-menu button {
  padding: 0.25rem 0.5rem;
  border: none;
  background: transparent;
  cursor: pointer;
  border-radius: 0.25rem;
}

.floating-menu button:hover {
  background-color: #f0f0f0;
}

.floating-menu button.is-active {
  background-color: #000;
  color: #fff;
}
</style>
```

### Custom shouldShow Logic

```vue theme={null}
<script setup>
import { useEditor, EditorContent, FloatingMenu } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

const editor = useEditor({
  content: '<p>Custom visibility logic</p>',
  extensions: [StarterKit],
})

// Only show the floating menu when in an empty paragraph that's not a heading
function shouldShow({ editor, state }) {
  const { $from } = state.selection
  const isEmptyParagraph = $from.parent.type.name === 'paragraph' 
    && $from.parent.content.size === 0
  const isHeading = editor.isActive('heading')
  
  return isEmptyParagraph && !isHeading
}
</script>

<template>
  <div>
    <EditorContent :editor="editor" />
    <FloatingMenu 
      :editor="editor" 
      v-if="editor"
      :shouldShow="shouldShow"
    >
      <button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()">
        Turn into H1
      </button>
      <button @click="editor.chain().focus().toggleBulletList().run()">
        Turn into List
      </button>
    </FloatingMenu>
  </div>
</template>
```

### With Tippy Options

```vue theme={null}
<script setup>
import { useEditor, EditorContent, FloatingMenu } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

const editor = useEditor({
  content: '<p>Positioned floating menu</p>',
  extensions: [StarterKit],
})

const tippyOptions = {
  placement: 'left',
  duration: 100,
  offset: [0, 10],
  zIndex: 1000,
}
</script>

<template>
  <div>
    <EditorContent :editor="editor" />
    <FloatingMenu 
      :editor="editor" 
      v-if="editor"
      :options="tippyOptions"
    >
      <div class="menu-content">
        <button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()">
          H1
        </button>
        <button @click="editor.chain().focus().toggleHeading({ level: 2 }).run()">
          H2
        </button>
      </div>
    </FloatingMenu>
  </div>
</template>
```

### Insert Media Menu

```vue theme={null}
<script setup>
import { useEditor, EditorContent, FloatingMenu } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'

const editor = useEditor({
  content: '<p>Insert media on empty lines</p>',
  extensions: [
    StarterKit,
    Image,
  ],
})

function addImage() {
  const url = window.prompt('Enter image URL')
  if (url) {
    editor.value?.chain().focus().setImage({ src: url }).run()
  }
}

function addCodeBlock() {
  editor.value?.chain().focus().toggleCodeBlock().run()
}
</script>

<template>
  <div>
    <EditorContent :editor="editor" />
    <FloatingMenu :editor="editor" v-if="editor" class="floating-menu">
      <button @click="addImage">
        Add Image
      </button>
      <button @click="addCodeBlock">
        Add Code Block
      </button>
      <button @click="editor.chain().focus().toggleBulletList().run()">
        Add List
      </button>
    </FloatingMenu>
  </div>
</template>
```

### Multiple Floating Menus

```vue theme={null}
<script setup>
import { useEditor, EditorContent, FloatingMenu } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

const editor = useEditor({
  content: '<p>Different menus for different contexts</p>',
  extensions: [StarterKit],
})

function shouldShowTextMenu({ editor }) {
  return editor.isActive('paragraph')
}

function shouldShowHeadingMenu({ editor }) {
  return editor.isActive('heading')
}
</script>

<template>
  <div>
    <EditorContent :editor="editor" />
    
    <!-- Text options menu -->
    <FloatingMenu 
      :editor="editor" 
      v-if="editor"
      pluginKey="textMenu"
      :shouldShow="shouldShowTextMenu"
    >
      <button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()">
        H1
      </button>
      <button @click="editor.chain().focus().toggleBulletList().run()">
        List
      </button>
    </FloatingMenu>
    
    <!-- Heading options menu -->
    <FloatingMenu 
      :editor="editor" 
      v-if="editor"
      pluginKey="headingMenu"
      :shouldShow="shouldShowHeadingMenu"
    >
      <button @click="editor.chain().focus().setParagraph().run()">
        Turn into Paragraph
      </button>
    </FloatingMenu>
  </div>
</template>
```

## Notes

* The floating menu is automatically positioned near the cursor using Tippy.js
* By default, it shows only when the cursor is in an empty paragraph
* The component renders as a `<div>` element that you can style with classes or inline styles
* Multiple floating menus can coexist by using different `pluginKey` values
* The menu is removed from the DOM and re-parented by the plugin when shown
* Use `shouldShow` to customize when the menu appears based on editor state
* The floating menu is ideal for slash commands or block-level content insertion

## See Also

* [BubbleMenu](/api/vue/bubble-menu) - Contextual menu for selected text
* [useEditor](/api/vue/use-editor) - Create editor instances
* [EditorContent](/api/vue/editor-content) - Render the editor
