Quick start
Add BubbleMenu.Default as a child of EditorProvider to get a fully-featured formatting toolbar.
import { StarterKit } from '@react-email/editor/extensions' ;
import { BubbleMenu } from '@react-email/editor/ui' ;
import { EditorProvider } from '@tiptap/react' ;
import '@react-email/editor/themes/default.css' ;
const extensions = [ StarterKit ];
export function MyEditor () {
return (
< EditorProvider extensions = { extensions } content = { content } >
< BubbleMenu.Default />
</ EditorProvider >
);
}
Select text to see the toolbar with formatting, alignment, node type selection, and link controls.
Excluding items
Hide specific items from the default bubble menu using excludeItems.
< BubbleMenu.Default excludeItems = { [ 'strike' , 'code' , 'uppercase' ] } />
All excludable item keys:
Key Description boldBold toggle italicItalic toggle underlineUnderline toggle strikeStrikethrough toggle codeInline code toggle uppercaseUppercase toggle align-leftLeft alignment align-centerCenter alignment align-rightRight alignment node-selectorBlock type selector (paragraph, headings, etc.) link-selectorLink add/edit control
Hiding on specific nodes or marks
Prevent the bubble menu from appearing on certain node types or when certain marks are active.
< BubbleMenu.Default
hideWhenActiveNodes = { [ 'codeBlock' , 'button' ] }
hideWhenActiveMarks = { [ 'link' ] }
/>
This is useful when combining the text bubble menu with contextual menus for links, images, or buttons — each gets its own menu via BubbleMenu.Root.
import { BubbleMenu , bubbleMenuTriggers } from '@react-email/editor/ui' ;
import { PluginKey } from '@tiptap/pm/state' ;
const linkPluginKey = new PluginKey ( 'linkBubbleMenu' );
<>
{ /* Hide text bubble menu on links and buttons -- their own menus handle those */ }
< BubbleMenu.Default hideWhenActiveNodes = { [ 'button' ] } hideWhenActiveMarks = { [ 'link' ] } />
< BubbleMenu.Root
trigger = { bubbleMenuTriggers . nodeWithoutSelection ( 'link' ) }
pluginKey = { linkPluginKey }
>
< BubbleMenu.LinkToolbar >
< BubbleMenu.LinkEditLink />
< BubbleMenu.LinkOpenLink />
< BubbleMenu.LinkUnlink />
</ BubbleMenu.LinkToolbar >
</ BubbleMenu.Root >
</>
Composing from primitives
For full control, build a custom bubble menu using the compound component API.
import { StarterKit } from '@react-email/editor/extensions' ;
import { BubbleMenu } from '@react-email/editor/ui' ;
import { EditorProvider } from '@tiptap/react' ;
export function MyEditor () {
return (
< EditorProvider extensions = { [ StarterKit ] } content = { content } >
< BubbleMenu.Root >
< BubbleMenu.ItemGroup >
< BubbleMenu.Bold />
< BubbleMenu.Italic />
< BubbleMenu.Underline />
</ BubbleMenu.ItemGroup >
< BubbleMenu.ItemGroup >
< BubbleMenu.AlignLeft />
< BubbleMenu.AlignCenter />
< BubbleMenu.AlignRight />
</ BubbleMenu.ItemGroup >
</ BubbleMenu.Root >
</ EditorProvider >
);
}
BubbleMenu.Root wraps everything, BubbleMenu.ItemGroup creates visual groupings,
and individual items render the toggle buttons.
Available items
Component Description BubbleMenu.BoldBold toggle BubbleMenu.ItalicItalic toggle BubbleMenu.UnderlineUnderline toggle BubbleMenu.StrikeStrikethrough toggle BubbleMenu.CodeInline code toggle BubbleMenu.UppercaseUppercase toggle BubbleMenu.AlignLeftLeft alignment BubbleMenu.AlignCenterCenter alignment BubbleMenu.AlignRightRight alignment BubbleMenu.NodeSelectorBlock type dropdown (paragraph, h1-h3, etc.) BubbleMenu.LinkSelectorLink add/edit popover BubbleMenu.SeparatorVisual separator between groups
Placement and offset
Control where the bubble menu appears relative to the selection.
< BubbleMenu.Root placement = "top" offset = { 12 } >
{ /* items */ }
</ BubbleMenu.Root >
placement
'top' | 'bottom'
default: "'bottom'"
Whether the menu appears above or below the selection.
Distance from the selection in pixels.
Examples
See bubble menus in action with runnable examples:
Bubble Menu Default bubble menu with text selection.
Custom Bubble Menu Composing a custom menu from primitives.