> ## 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.

# BubbleMenu

> Display a context menu that appears when text is selected, positioned using Floating UI

The BubbleMenu extension provides a floating menu that appears when text is selected in the editor. It's positioned intelligently using Floating UI and supports custom positioning, visibility rules, and framework-specific components for React and Vue.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @tiptap/extension-bubble-menu
  ```

  ```bash yarn theme={null}
  yarn add @tiptap/extension-bubble-menu
  ```

  ```bash pnpm theme={null}
  pnpm add @tiptap/extension-bubble-menu
  ```
</CodeGroup>

## Basic Usage

### Vanilla JavaScript

```javascript theme={null}
import { Editor } from '@tiptap/core'
import BubbleMenu from '@tiptap/extension-bubble-menu'

const menu = document.querySelector('#bubble-menu')

const editor = new Editor({
  extensions: [
    BubbleMenu.configure({
      element: menu,
    }),
  ],
})
```

### React

```tsx theme={null}
import { BubbleMenu, useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'

function Editor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: '<p>Select some text to see the bubble menu</p>',
  })

  return (
    <>
      <EditorContent editor={editor} />
      <BubbleMenu editor={editor}>
        <button onClick={() => editor.chain().focus().toggleBold().run()}>
          Bold
        </button>
        <button onClick={() => editor.chain().focus().toggleItalic().run()}>
          Italic
        </button>
      </BubbleMenu>
    </>
  )
}
```

### Vue

```vue theme={null}
<template>
  <editor-content :editor="editor" />
  <bubble-menu :editor="editor" v-if="editor">
    <button @click="editor.chain().focus().toggleBold().run()">
      Bold
    </button>
    <button @click="editor.chain().focus().toggleItalic().run()">
      Italic
    </button>
  </bubble-menu>
</template>

<script>
import { Editor, EditorContent } from '@tiptap/vue-3'
import { BubbleMenu } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

export default {
  components: {
    EditorContent,
    BubbleMenu,
  },
  data() {
    return {
      editor: null,
    }
  },
  mounted() {
    this.editor = new Editor({
      extensions: [StarterKit],
      content: '<p>Select some text to see the bubble menu</p>',
    })
  },
  beforeUnmount() {
    this.editor.destroy()
  },
}
</script>
```

## Configuration Options

<ParamField path="element" type="HTMLElement" required>
  The DOM element that contains your menu.

  **Default:** `null`
</ParamField>

<ParamField path="pluginKey" type="PluginKey | string">
  The plugin key for the bubble menu.

  **Default:** `'bubbleMenu'`
</ParamField>

<ParamField path="updateDelay" type="number">
  The delay in milliseconds before the menu position should be updated. This can be useful to prevent performance issues.

  **Default:** `250`
</ParamField>

<ParamField path="resizeDelay" type="number">
  The delay in milliseconds before the menu position should be updated on window resize.

  **Default:** `60`
</ParamField>

<ParamField path="shouldShow" type="function">
  A function that determines whether the menu should be shown or not. If this function returns `false`, the menu will be hidden, otherwise it will be shown.

  **Parameters:**

  * `editor`: The editor instance
  * `element`: The menu element
  * `view`: The ProseMirror EditorView
  * `state`: The current editor state
  * `oldState`: The previous editor state (optional)
  * `from`: Selection start position
  * `to`: Selection end position

  **Default:** Shows when text is selected and editor has focus

  ```javascript theme={null}
  BubbleMenu.configure({
    element: menu,
    shouldShow: ({ editor, view, state, from, to }) => {
      // Only show for text selections
      return from !== to && !state.selection.empty
    },
  })
  ```
</ParamField>

<ParamField path="appendTo" type="HTMLElement | (() => HTMLElement)">
  The DOM element to append your menu to. Sometimes the menu needs to be appended to a different DOM context due to accessibility, clipping, or z-index issues.

  **Default:** Editor's parent element

  ```javascript theme={null}
  BubbleMenu.configure({
    element: menu,
    appendTo: () => document.body,
  })
  ```
</ParamField>

<ParamField path="getReferencedVirtualElement" type="() => VirtualElement | null">
  A function that returns the virtual element for the menu. This is useful when the menu needs to be positioned relative to a specific DOM element.

  ```javascript theme={null}
  BubbleMenu.configure({
    element: menu,
    getReferencedVirtualElement: () => ({
      getBoundingClientRect: () => customElement.getBoundingClientRect(),
    }),
  })
  ```
</ParamField>

<ParamField path="options" type="object">
  Configuration options passed to Floating UI for positioning. See [Floating UI documentation](https://floating-ui.com/docs/computePosition#options) for full details.

  **Properties:**

  <ParamField path="options.strategy" type="'absolute' | 'fixed'">
    Positioning strategy.

    **Default:** `'absolute'`
  </ParamField>

  <ParamField path="options.placement" type="string">
    Menu placement relative to selection. Options: `'top'`, `'right'`, `'bottom'`, `'left'`, `'top-start'`, `'top-end'`, `'right-start'`, `'right-end'`, `'bottom-start'`, `'bottom-end'`, `'left-start'`, `'left-end'`

    **Default:** `'top'`
  </ParamField>

  <ParamField path="options.offset" type="number | object | boolean">
    Distance in pixels from the selection.

    **Default:** `8`

    ```javascript theme={null}
    BubbleMenu.configure({
      element: menu,
      options: {
        offset: 16,
      },
    })
    ```
  </ParamField>

  <ParamField path="options.flip" type="object | boolean">
    Whether to flip the menu to the opposite side if there's not enough space.

    **Default:** `{}`
  </ParamField>

  <ParamField path="options.shift" type="object | boolean">
    Whether to shift the menu along the axis to keep it in view.

    **Default:** `{}`
  </ParamField>

  <ParamField path="options.scrollTarget" type="HTMLElement | Window">
    The scrollable element that should be listened to when updating the position of the bubble menu.

    **Default:** `window`

    ```javascript theme={null}
    BubbleMenu.configure({
      element: menu,
      options: {
        scrollTarget: document.querySelector('.scroll-container'),
      },
    })
    ```
  </ParamField>

  <ParamField path="options.onShow" type="() => void">
    Callback fired when the menu is shown.
  </ParamField>

  <ParamField path="options.onHide" type="() => void">
    Callback fired when the menu is hidden.
  </ParamField>

  <ParamField path="options.onUpdate" type="() => void">
    Callback fired when the menu position is updated.
  </ParamField>

  <ParamField path="options.onDestroy" type="() => void">
    Callback fired when the menu is destroyed.
  </ParamField>
</ParamField>

## React Component Props

When using the React component, you can pass these props:

<ParamField path="editor" type="Editor">
  The editor instance. Can be omitted if used inside `EditorProvider`.
</ParamField>

<ParamField path="pluginKey" type="string">
  The plugin key.

  **Default:** `'bubbleMenu'`
</ParamField>

<ParamField path="updateDelay" type="number">
  Update delay in milliseconds.
</ParamField>

<ParamField path="resizeDelay" type="number">
  Resize delay in milliseconds.
</ParamField>

<ParamField path="shouldShow" type="function">
  Function to determine visibility.
</ParamField>

<ParamField path="options" type="object">
  Floating UI configuration options.
</ParamField>

Plus all standard HTML div attributes (className, style, etc.).

## Vue Component Props

<ParamField path="editor" type="Editor" required>
  The editor instance.
</ParamField>

<ParamField path="pluginKey" type="string | Object">
  The plugin key.

  **Default:** `'bubbleMenu'`
</ParamField>

<ParamField path="updateDelay" type="number">
  Update delay in milliseconds.
</ParamField>

<ParamField path="resizeDelay" type="number">
  Resize delay in milliseconds.
</ParamField>

<ParamField path="shouldShow" type="function">
  Function to determine visibility.
</ParamField>

<ParamField path="options" type="object">
  Floating UI configuration options.
</ParamField>

<ParamField path="appendTo" type="HTMLElement | Function">
  Element to append the menu to.
</ParamField>

<ParamField path="getReferencedVirtualElement" type="function">
  Function to get the virtual element for positioning.
</ParamField>

## Advanced Examples

### Custom Visibility Logic

```javascript theme={null}
BubbleMenu.configure({
  element: menu,
  shouldShow: ({ editor, state, from, to }) => {
    // Only show bubble menu for links
    return editor.isActive('link')
  },
})
```

### Update Position Programmatically

```javascript theme={null}
// Trigger a position update
editor.view.dispatch(
  editor.state.tr.setMeta('bubbleMenu', 'updatePosition')
)
```

### Multiple Bubble Menus

```tsx theme={null}
function Editor() {
  const editor = useEditor({
    extensions: [StarterKit, Link],
  })

  return (
    <>
      <EditorContent editor={editor} />
      
      {/* Text formatting menu */}
      <BubbleMenu
        editor={editor}
        pluginKey="textMenu"
        shouldShow={({ editor, from, to }) => {
          return from !== to && !editor.isActive('link')
        }}
      >
        <button onClick={() => editor.chain().focus().toggleBold().run()}>
          Bold
        </button>
        <button onClick={() => editor.chain().focus().toggleItalic().run()}>
          Italic
        </button>
      </BubbleMenu>
      
      {/* Link-specific menu */}
      <BubbleMenu
        editor={editor}
        pluginKey="linkMenu"
        shouldShow={({ editor }) => editor.isActive('link')}
      >
        <button onClick={() => editor.chain().focus().unsetLink().run()}>
          Remove Link
        </button>
      </BubbleMenu>
    </>
  )
}
```

### Custom Positioning

```javascript theme={null}
BubbleMenu.configure({
  element: menu,
  options: {
    placement: 'bottom',
    offset: 20,
    flip: {
      fallbackPlacements: ['top', 'right', 'left'],
    },
    shift: {
      padding: 8,
    },
  },
})
```

## Source Code

View the source code on GitHub:

* [Extension](https://github.com/ueberdosis/tiptap/tree/main/packages/extension-bubble-menu/src/bubble-menu.ts:19)
* [Plugin](https://github.com/ueberdosis/tiptap/tree/main/packages/extension-bubble-menu/src/bubble-menu-plugin.ts:151)
* [React Component](https://github.com/ueberdosis/tiptap/tree/main/packages/react/src/menus/BubbleMenu.tsx:11)
* [Vue Component](https://github.com/ueberdosis/tiptap/tree/main/packages/vue-3/src/menus/BubbleMenu.ts:6)
