Skip to content

Bubble Menu

Bubble menus provide actions near selected text or a selected node. They are separate React components: register the corresponding extensions, then mount the menus inside the same RichTextProvider as the document.

Importing a menu does not mount it, and mounting a menu does not register its extension.

Add a text selection menu

This example uses the packages from Getting Started. Select a word in the editor to show the menu:

tsx
'use client';

import { EditorContent, useEditor } from '@tiptap/react';
import { Document } from '@tiptap/extension-document';
import { Paragraph } from '@tiptap/extension-paragraph';
import { Text } from '@tiptap/extension-text';
import { RichTextProvider } from 'reactjs-tiptap-editor';
import { Bold, RichTextBold } from 'reactjs-tiptap-editor/bold';
import { Italic, RichTextItalic } from 'reactjs-tiptap-editor/italic';
import { RichTextBubbleText } from 'reactjs-tiptap-editor/bubble/text';
import 'reactjs-tiptap-editor/style.css';

const extensions = [Document, Paragraph, Text, Bold, Italic];

export default function BubbleMenuExample() {
  const editor = useEditor({
    extensions,
    content: '<p>Select a few words to format them.</p>',
    immediatelyRender: false,
  });

  if (!editor) return null;

  return (
    <RichTextProvider editor={editor}>
      <RichTextBubbleText
        buttonBubble={
          <>
            <RichTextBold />
            <RichTextItalic />
          </>
        }
      />
      <EditorContent editor={editor} />
    </RichTextProvider>
  );
}

buttonBubble replaces the default text controls with your own React content. Each control still needs its corresponding extension. Omit the prop to use the library's default text menu; register the formatting and block features you intend to offer there.

A regular toolbar and a bubble menu can coexist. Both operate on the same editor instance.

Add a node menu

For images, register Image in the existing extension array and mount RichTextBubbleImage under the provider. Select an inserted image to show its controls. Follow the same pattern for the other supported nodes:

Menu componentRequired featurePurpose
RichTextBubbleTextText and the formatting extensions used by its controlsFormat selected text.
RichTextBubbleLinkLinkEdit an existing link.
RichTextBubbleImageImageEdit a selected image.
RichTextBubbleVideoVideoEdit a selected video.
RichTextBubbleTableTableEdit table structure and cells.
RichTextBubbleIframeIframeEdit an embedded frame.
RichTextBubbleColumnsColumn and companion nodesManage column layouts.
RichTextBubbleImageGifImageGifEdit a selected GIF.
RichTextBubbleDrawerDrawerEdit a drawing node.
RichTextBubbleExcalidrawExcalidrawEdit an Excalidraw node.
RichTextBubbleMermaidMermaidEdit a diagram node.
RichTextBubbleTwitterTwitterManage a post embed.
RichTextBubbleCalloutCalloutEdit a callout.
RichTextBubbleKatexKatexEdit a mathematical expression.
RichTextBubbleCodeBlockCodeBlockAccess code-block actions.

All menu components in this table are exported from reactjs-tiptap-editor/bubble. Mount each menu once per editor and only include the menus your editor needs.

Individual imports

Use these public subpaths to make feature dependencies explicit. The existing /bubble entry remains supported. Import only the components you mount.

ComponentSubpath after reactjs-tiptap-editor
RichTextBubbleText/bubble/text
RichTextBubbleMenuDragHandle/bubble/drag-handle
RichTextBubbleColumns/bubble/columns
RichTextBubbleCodeBlock/bubble/codeblock
RichTextAIImprove/bubble/ai
RichTextBubbleCallout/bubble/callout
RichTextBubbleDrawer/bubble/drawer
RichTextBubbleExcalidraw/bubble/excalidraw
RichTextBubbleIframe/bubble/iframe
RichTextBubbleKatex/bubble/katex
RichTextBubbleLink/bubble/link
RichTextBubbleMermaid/bubble/mermaid
RichTextBubbleTable/bubble/table
RichTextBubbleTwitter/bubble/twitter
RichTextBubbleImage/bubble/media
RichTextBubbleVideo/bubble/media
RichTextBubbleImageGif/bubble/media

/bubble/media exports the Image, Video, and ImageGif menus together. RichTextAIImprove is an AI control; see AI. The text bubble does not require KaTeX or Yjs. The drag handle still brings collaboration-related dependencies through Tiptap, even in an editor without collaboration.

Block drag handle

RichTextBubbleMenuDragHandle provides a handle for moving document blocks and a block action menu. It does not move the bubble menu itself.

tsx
import { RichTextBubbleMenuDragHandle } from 'reactjs-tiptap-editor/bubble/drag-handle';

// Mount inside your existing RichTextProvider.
<RichTextBubbleMenuDragHandle />;

Slash commands

SlashCommandList supplies the slash command list and is not a text-selection bubble menu. Mount it inside the provider and register SlashCommand to enable / commands. See Slash Command.

Troubleshooting

If a menu does not appear, confirm that the editor is editable, its matching extension is registered, and the appropriate content is selected. A collapsed text cursor does not show the text-selection menu. Check clipping or stacking styles in your host layout if a menu appears behind another element.

Made with ❤️