---
title: "BubbleMenu extension"
description: "Add a menu toolbar that pops up above your Tiptap editor’s text content. Learn more in our Documentation!"
canonical_url: "https://tiptap.dev/docs/editor/extensions/functionality/bubble-menu"
---

# BubbleMenu extension

Add a menu toolbar that pops up above your Tiptap editor’s text content. Learn more in our Documentation!

This extension will make a contextual menu appear near a selection of text. Use it to let users apply [marks](https://tiptap.dev/docs/editor/extensions/marks.md) to their text selection.

As always, the markup and styling is totally up to you.

> **Interactive demo:** [BubbleMenu](https://embed.tiptap.dev/preview/Extensions/BubbleMenu)

> **Warning:**
>
> If you are using a framework like React or Vue, use the frameworks specific `BubbleMenu` component instead of the extension. The component provides a more convenient API and handles the DOM element for you. You can find more information about the component in the [Usage with frameworks](#usage-with-frameworks) section below.

## Install

```bash
npm install @tiptap/extension-bubble-menu
```

## Settings

### element

The DOM element that contains your menu.

Type: `HTMLElement`

Default: `null`

In the React version of the `Bubble Menu`, access the DOM element with the `ref` prop of the `BubbleMenu` component, by [passing a ref](https://react.dev/learn/manipulating-the-dom-with-refs) into it.

### updateDelay

The `BubbleMenu` debounces the `update` method to allow the bubble menu to not be updated on every selection update. This can be controlled in milliseconds.
The `BubbleMenuPlugin` will come with a default delay of 250ms. This can be deactivated, by setting the delay to `0` which deactivates the debounce.

Type: `Number`

Default: `undefined`

### resizeDelay

The `BubbleMenu` debounces the resize calculation for the bubble menu to allow the bubble menu to not be updated on every resize event. This can be controlled in milliseconds.

Type: `Number`

Default: `100`

### options

Under the hood, the `BubbleMenu` [Floating UI](https://floating-ui.com). You can control the middleware and positioning of the floating menu with these options.

Type: `Object`

Default: `{ strategy: 'absolute', placement: 'right' }`

| Option          | Type                                   | Description                                                                                                                                                  |
| --------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `strategy`      | `string`                               | The positioning strategy. See [here](https://floating-ui.com/docs/computePosition#strategy)                                                                  |
| `placement`     | `string`                               | The placement of the menu. See [here](https://floating-ui.com/docs/computePosition#placement)                                                                |
| `offset`        | `number`, `OffsetOptions` or `boolean` | The [offset middleware options](https://floating-ui.com/docs/offset#options). If `true` use default options, if `false` disable the middleware               |
| `flip`          | `FlipOptions` or `boolean`             | The [flip middleware options](https://floating-ui.com/docs/flip#options). If `true` use default options, if `false` disable the middleware                   |
| `shift`         | `ShiftOptions` or `boolean`            | The [shift middleware options](https://floating-ui.com/docs/shift#options). If `true` use default options, if `false` disable the middleware                 |
| `arrow`         | `ArrowOptions` or `false`              | The [arrow middleware options](https://floating-ui.com/docs/arrow#options). If `false` disable the middleware                                                |
| `size`          | `SizeOptions` or `boolean`             | The [size middleware options](https://floating-ui.com/docs/size#options). If `true` use default options, if `false` disable the middleware                   |
| `autoPlacement` | `AutoPlacementOptions` or `boolean`    | The [autoPlacement middleware options](https://floating-ui.com/docs/autoPlacement#options). If `true` use default options, if `false` disable the middleware |
| `hide`          | `HideOptions` or `boolean`             | The [hide middleware options](https://floating-ui.com/docs/hide#options). If `true` use default options, if `false` disable the middleware                   |
| `inline`        | `InlineOptions` or `boolean`           | The [inline middleware options](https://floating-ui.com/docs/inline#options). If `true` use default options, if `false` disable the middleware               |
| `onShow`        | `Function` or `undefined`              | A callback that is called when the menu is shown. This can be used to add custom logic or styles when the menu is displayed.                                 |
| `onHide`        | `Function` or `undefined`              | A callback that is called when the menu is hidden. This can be used to add custom logic or styles when the menu is hidden.                                   |
| `onUpdate`      | `Function` or `undefined`              | A callback that is called when the menu is updated. This can be used to add custom logic or styles when the menu is updated.                                 |
| `onDestroy`     | `Function` or `undefined`              | A callback that is called when the menu is destroyed. This can be used to add custom logic or styles when the menu is removed.                               |

### pluginKey

The key for the underlying ProseMirror plugin. Make sure to use different keys if you add more than one instance.

Type: `string | PluginKey`

Default: `'bubbleMenu'`

### shouldShow

A callback to control whether the menu should be shown or not.

Type: `(props) => boolean`

### appendTo

The element to which the bubble menu should be appended to in the DOM. Can be a `HTMLElement` or a callback function that returns a `HTMLElement`.

Type: `HTMLElement | (() => HTMLElement) | undefined`

Default: `undefined`, the menu will be appended to the editor's parent element (`editor.view.dom.parentElement`).

### getReferencedVirtualElement

A callback to provide the anchor coordinates used to position the menu. Should return a [virtual element](https://floating-ui.com/docs/virtual-elements) as expected by [Floating UI](https://floating-ui.com/).

Type: `() => VirtualElement | null`

Default: `null`, anchor is implied by the editor selection.

## Source code

[packages/extension-bubble-menu/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-bubble-menu/)

## Use the extension

### JavaScript

```js
import { Editor } from '@tiptap/core'
import BubbleMenu from '@tiptap/extension-bubble-menu'

new Editor({
  element: document.querySelector('#editor'),
  extensions: [
    StarterKit,
    BubbleMenu.configure({
      element: document.querySelector('#bubble-menu'),
    }),
  ],
  content: '<p>Select some text to see the bubble menu.</p>',
})
```

Add a menu element to your HTML:

```html
<div id="bubble-menu" style="display: none; position: absolute;">
  <button onclick="editor.chain().focus().toggleBold().run()">Bold</button>
  <button onclick="editor.chain().focus().toggleItalic().run()">Italic</button>
</div>
<div id="editor"></div>
```

Tiptap will automatically show and position the menu element when text is selected.

## Usage with frameworks

### React

The `@tiptap/react` package comes with a `BubbleMenu` component you can import from `@tiptap/react/menus`. It provides the same functionality as the extension but with a React-friendly API. When using this component, you don't need to add the `BubbleMenu` extension to your editor.

```jsx
import { BubbleMenu } from '@tiptap/react/menus'

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

### Vue

The `@tiptap/vue-3` package comes with a `BubbleMenu` component you can import from `@tiptap/vue-3/menus`. It provides the same functionality as the extension but with a Vue-friendly API. When using this component, you don't need to add the `BubbleMenu` extension to your editor.

```vue
<template>
  <BubbleMenu :editor="editor">
    <button @click="editor.chain().focus().toggleBold().run()">
      Bold
    </button>
    <button @click="editor.chain().focus().toggleItalic().run()">
      Italic
    </button>
  </BubbleMenu>
</template>

<script setup>
  import { BubbleMenu } from '@tiptap/vue-3/menus'
  
  // make sure to pass the editor instance as a prop to the BubbleMenu component
  const { editor } = defineProps({
    editor: {
      type: Object,
      required: true,
    },
  })
</script>
```

**Note**: The same menu is also available in the Vue 2 version of Tiptap, you can import it from `@tiptap/vue-2/menus`.

### Custom logic

Customize the logic for showing the menu with the `shouldShow` option. For components, `shouldShow` can be passed as a prop.

```js
BubbleMenu.configure({
  shouldShow: ({ editor, view, state, oldState, from, to }) => {
    // only show the bubble menu for images and links
    return editor.isActive('image') || editor.isActive('link')
  },
})
```

### Multiple menus

Use multiple menus by setting an unique `pluginKey`.

```js
import { Editor } from '@tiptap/core'
import BubbleMenu from '@tiptap/extension-bubble-menu'

new Editor({
  extensions: [
    BubbleMenu.configure({
      pluginKey: 'bubbleMenuOne',
      element: document.querySelector('.menu-one'),
    }),
    BubbleMenu.configure({
      pluginKey: 'bubbleMenuTwo',
      element: document.querySelector('.menu-two'),
    }),
  ],
})
```

Alternatively you can pass a ProseMirror `PluginKey`.

```js
import { Editor } from '@tiptap/core'
import BubbleMenu from '@tiptap/extension-bubble-menu'
import { PluginKey } from '@tiptap/pm/state'

new Editor({
  extensions: [
    BubbleMenu.configure({
      pluginKey: new PluginKey('bubbleMenuOne'),
      element: document.querySelector('.menu-one'),
    }),
    BubbleMenu.configure({
      pluginKey: new PluginKey('bubbleMenuTwo'),
      element: document.querySelector('.menu-two'),
    }),
  ],
})
```

### Force update the position of the bubble menu

If the bubble menu changes size after the initial render, its position will not be adjusted automatically. To fix this, you can force update the position by setting a `pluginKey` on the extension and emitting an `'updatePosition'` event with that key.

```ts
BubbleMenu.configure({
  pluginKey: 'myBubbleMenu',
  element: document.querySelector('.menu'),
})

editor.commands.setMeta('myBubbleMenu', 'updatePosition')
```

To target a specific bubble menu, pass that menu's `pluginKey` instead.

```ts
editor.commands.setMeta('bubbleMenuOne', 'updatePosition')
```

If you use the React or Vue `BubbleMenu` component and want to trigger `updatePosition` externally, pass an explicit `pluginKey` prop to the component first. If you omit `pluginKey`, the component creates its own ProseMirror `PluginKey`, which external code cannot reliably reference later.

### Programmatically show or hide the bubble menu

You can programmatically show or hide the bubble menu by setting a `pluginKey` on the extension and dispatching a transaction with the `'show'` or `'hide'` meta value using that key.

```ts
BubbleMenu.configure({
  pluginKey: 'myBubbleMenu',
  element: document.querySelector('.menu'),
})

// Show the bubble menu
editor.commands.setMeta('myBubbleMenu', 'show')

// Hide the bubble menu
editor.commands.setMeta('myBubbleMenu', 'hide')
```

**Note:** dispatching `'show'` or `'hide'` will override the behavior defined in `shouldShow`.
