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

> Menu component that appears on empty lines in the editor

## Overview

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

## Type Signature

```typescript theme={null}
const FloatingMenu: React.ForwardRefExoticComponent<
  FloatingMenuProps & React.RefAttributes<HTMLDivElement>
>

type FloatingMenuProps = Omit<FloatingMenuPluginProps, 'element' | 'editor'> & {
  editor: Editor | null
  options?: FloatingMenuPluginProps['options']
} & React.HTMLAttributes<HTMLDivElement>
```

## Props

<ParamField path="editor" type="Editor | null" required>
  The editor instance. If not provided, the component will attempt to use the editor from React context via `useCurrentEditor`.
</ParamField>

<ParamField path="pluginKey" type="string | PluginKey" default="'floatingMenu'">
  Unique identifier for this floating menu plugin. Use different keys if you have multiple floating menus.
</ParamField>

<ParamField path="updateDelay" type="number" default="250">
  Delay in milliseconds before the menu position is updated. This debounces position updates for better performance.
</ParamField>

<ParamField path="resizeDelay" type="number" default="60">
  Delay in milliseconds before the menu position is updated on window resize. This debounces resize events for better performance.
</ParamField>

<ParamField path="shouldShow" type="function | null" default="null">
  Function that determines whether the menu should be shown. Receives editor state and selection information.

  ```typescript theme={null}
  shouldShow?: (props: {
    editor: Editor
    view: EditorView
    state: EditorState
    oldState?: EditorState
    from: number
    to: number
  }) => boolean
  ```

  Default behavior: Shows when the cursor is in an empty text block at the root level and the editor has focus.
</ParamField>

<ParamField path="appendTo" type="HTMLElement | (() => HTMLElement)">
  The DOM element to append the menu to. Useful when you need to render the menu in a specific container for z-index or overflow reasons.

  Default: The editor's parent element.
</ParamField>

<ParamField path="options" type="object">
  Floating UI configuration options for positioning and behavior.

  <Expandable title="properties">
    <ParamField path="strategy" type="'absolute' | 'fixed'" default="'absolute'">
      Positioning strategy for the menu.
    </ParamField>

    <ParamField path="placement" type="Placement" default="'right'">
      Preferred placement of the menu relative to the cursor. Options: `'top'`, `'right'`, `'bottom'`, `'left'`, or any `-start`/`-end` variant.
    </ParamField>

    <ParamField path="offset" type="number | object | boolean" default="8">
      Offset from the reference element in pixels. Can be a number, detailed offset configuration, or `false` to disable.
    </ParamField>

    <ParamField path="flip" type="object | boolean" default="{}">
      Enable flipping to the opposite placement if there's not enough space. Pass an object for configuration or `false` to disable.
    </ParamField>

    <ParamField path="shift" type="object | boolean" default="{}">
      Enable shifting along the axis to keep the menu in view. Pass an object for configuration or `false` to disable.
    </ParamField>

    <ParamField path="arrow" type="object | false" default="false">
      Configuration for an arrow element pointing to the reference. Pass an object with `element` reference or `false` to disable.
    </ParamField>

    <ParamField path="size" type="object | boolean" default="false">
      Enable size adjustments to fit the menu within the viewport. Pass an object for configuration or `false` to disable.
    </ParamField>

    <ParamField path="autoPlacement" type="object | boolean" default="false">
      Automatically choose the placement with the most space. Pass an object for configuration or `false` to disable.
    </ParamField>

    <ParamField path="hide" type="object | boolean" default="false">
      Hide the menu when the reference is hidden or has escaped the boundary. Pass an object for configuration or `false` to disable.
    </ParamField>

    <ParamField path="inline" type="object | boolean" default="false">
      Improve positioning for inline reference elements. Pass an object for configuration or `false` to disable.
    </ParamField>

    <ParamField path="scrollTarget" type="HTMLElement | Window" default="window">
      The scrollable element to listen to for scroll events. The menu position will update when this element is scrolled.
    </ParamField>

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

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

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

    <ParamField path="onDestroy" type="() => void">
      Callback function called when the menu is destroyed.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="children" type="React.ReactNode" required>
  The content to render inside the floating menu.
</ParamField>

<ParamField path="...props" type="React.HTMLAttributes<HTMLDivElement>">
  All standard HTML div attributes (className, style, etc.) are supported.
</ParamField>

## Usage Examples

### Basic Usage

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

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: '<p>Click on an empty line to see the floating menu</p>',
  })

  return (
    <>
      <EditorContent editor={editor} />
      <FloatingMenu editor={editor}>
        <button onClick={() => editor?.chain().focus().toggleHeading({ level: 1 }).run()}>
          H1
        </button>
        <button onClick={() => editor?.chain().focus().toggleBulletList().run()}>
          List
        </button>
      </FloatingMenu>
    </>
  )
}
```

### Block Type Selector

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

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
  })

  return (
    <>
      <EditorContent editor={editor} />
      <FloatingMenu
        editor={editor}
        className="floating-menu"
      >
        <button
          onClick={() => editor?.chain().focus().toggleHeading({ level: 1 }).run()}
        >
          Heading 1
        </button>
        <button
          onClick={() => editor?.chain().focus().toggleHeading({ level: 2 }).run()}
        >
          Heading 2
        </button>
        <button
          onClick={() => editor?.chain().focus().toggleBulletList().run()}
        >
          Bullet List
        </button>
        <button
          onClick={() => editor?.chain().focus().toggleOrderedList().run()}
        >
          Numbered List
        </button>
        <button
          onClick={() => editor?.chain().focus().toggleCodeBlock().run()}
        >
          Code Block
        </button>
      </FloatingMenu>
    </>
  )
}
```

### Styled Floating Menu

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

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
  })

  return (
    <>
      <EditorContent editor={editor} />
      <FloatingMenu
        editor={editor}
        style={{
          background: '#1a1a1a',
          color: 'white',
          padding: '8px',
          borderRadius: '8px',
          boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)',
        }}
      >
        <div style={{ display: 'flex', gap: '4px' }}>
          <button
            style={{
              padding: '8px 12px',
              background: 'transparent',
              border: '1px solid #444',
              color: 'white',
              borderRadius: '4px',
              cursor: 'pointer',
            }}
            onClick={() => editor?.chain().focus().toggleHeading({ level: 1 }).run()}
          >
            H1
          </button>
          <button
            style={{
              padding: '8px 12px',
              background: 'transparent',
              border: '1px solid #444',
              color: 'white',
              borderRadius: '4px',
              cursor: 'pointer',
            }}
            onClick={() => editor?.chain().focus().toggleBulletList().run()}
          >
            List
          </button>
        </div>
      </FloatingMenu>
    </>
  )
}
```

### Custom shouldShow Logic

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

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
  })

  return (
    <>
      <EditorContent editor={editor} />
      <FloatingMenu
        editor={editor}
        shouldShow={({ state, editor }) => {
          const { $anchor } = state.selection
          
          // Only show in paragraph nodes
          if ($anchor.parent.type.name !== 'paragraph') {
            return false
          }
          
          // Only show when empty and at root level
          return (
            $anchor.parent.childCount === 0 &&
            $anchor.depth === 1 &&
            editor.isEditable
          )
        }}
      >
        <button onClick={() => editor?.chain().focus().toggleHeading({ level: 1 }).run()}>
          H1
        </button>
      </FloatingMenu>
    </>
  )
}
```

### Slash Command Style

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

function MyEditor() {
  const [showSlashMenu, setShowSlashMenu] = useState(false)
  
  const editor = useEditor({
    extensions: [StarterKit],
    onUpdate: ({ editor }) => {
      const { $from } = editor.state.selection
      const text = $from.parent.textContent
      setShowSlashMenu(text === '/')
    },
  })

  const insertBlock = (type: string) => {
    if (!editor) return
    
    // Delete the slash
    editor.chain().focus().deleteRange({ from: editor.state.selection.from - 1, to: editor.state.selection.from }).run()
    
    // Insert the block
    switch (type) {
      case 'h1':
        editor.chain().toggleHeading({ level: 1 }).run()
        break
      case 'list':
        editor.chain().toggleBulletList().run()
        break
      case 'code':
        editor.chain().toggleCodeBlock().run()
        break
    }
  }

  return (
    <>
      <EditorContent editor={editor} />
      {showSlashMenu && (
        <FloatingMenu
          editor={editor}
          options={{ placement: 'bottom-start' }}
        >
          <div className="slash-menu">
            <button onClick={() => insertBlock('h1')}>Heading 1</button>
            <button onClick={() => insertBlock('list')}>Bullet List</button>
            <button onClick={() => insertBlock('code')}>Code Block</button>
          </div>
        </FloatingMenu>
      )}
    </>
  )
}
```

### With Icons

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

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
  })

  return (
    <>
      <EditorContent editor={editor} />
      <FloatingMenu editor={editor} className="floating-menu">
        <button
          onClick={() => editor?.chain().focus().toggleHeading({ level: 1 }).run()}
          title="Heading 1"
        >
          <svg width="16" height="16" viewBox="0 0 16 16">
            <path d="M2 3h2v10H2V3zm4 0h2v10H6V3zm6 0h2v10h-2V3z" />
          </svg>
        </button>
        <button
          onClick={() => editor?.chain().focus().toggleBulletList().run()}
          title="Bullet List"
        >
          <svg width="16" height="16" viewBox="0 0 16 16">
            <circle cx="2" cy="4" r="1" />
            <circle cx="2" cy="8" r="1" />
            <circle cx="2" cy="12" r="1" />
            <line x1="5" y1="4" x2="14" y2="4" />
            <line x1="5" y1="8" x2="14" y2="8" />
            <line x1="5" y1="12" x2="14" y2="12" />
          </svg>
        </button>
      </FloatingMenu>
    </>
  )
}
```

### Custom Positioning

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

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
  })

  return (
    <>
      <EditorContent editor={editor} />
      <FloatingMenu
        editor={editor}
        options={{
          placement: 'left-start',
          offset: 12,
          shift: { padding: 8 },
          onShow: () => console.log('Menu shown'),
          onHide: () => console.log('Menu hidden'),
        }}
      >
        <div>+ Add block</div>
      </FloatingMenu>
    </>
  )
}
```

## Styling

### CSS Example

```css theme={null}
.floating-menu {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px;
  background: white;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.floating-menu button {
  padding: 8px 12px;
  border: none;
  background: transparent;
  text-align: left;
  cursor: pointer;
  border-radius: 4px;
  transition: background 0.2s;
}

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

.floating-menu button:active {
  background: #e2e8f0;
}
```

### Notion-Style Plus Button

```css theme={null}
.floating-menu {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: white;
  border: 1px solid #e2e8f0;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.2s;
}

.floating-menu:hover {
  background: #f1f5f9;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.floating-menu::before {
  content: '+';
  font-size: 18px;
  font-weight: bold;
  color: #64748b;
}
```

## Important Notes

<Note>
  The FloatingMenu uses Floating UI for positioning, which provides intelligent placement that avoids overflow and adjusts to viewport constraints.
</Note>

<Warning>
  When clicking buttons inside the floating menu, make sure to use `.focus()` in your command chains to prevent the editor from losing focus, which would hide the menu.
</Warning>

<Tip>
  The default `shouldShow` behavior only shows the menu on empty text blocks at the root level. Customize this behavior with the `shouldShow` prop to match your use case.
</Tip>

## Default Behavior

By default, the floating menu:

* Shows when the cursor is in an empty text block at root depth
* Hides when text is typed
* Hides when the editor loses focus
* Positions itself to the right of the cursor
* Only appears when the editor is editable

The menu will NOT show:

* In empty list items
* In code blocks
* In nested content
* When text exists in the block
* When the editor doesn't have focus

## Comparison with BubbleMenu

| Feature          | FloatingMenu                | BubbleMenu         |
| ---------------- | --------------------------- | ------------------ |
| Appears on       | Empty lines                 | Text selection     |
| Default position | Right of cursor             | Above selection    |
| Common use       | Block formatting            | Text formatting    |
| Example actions  | Insert heading, list, media | Bold, italic, link |

## Related

* [BubbleMenu](/api/react/bubble-menu) - Menu for selected text
* [useEditor](/api/react/use-editor) - Hook for creating editor instances
* [Floating UI Documentation](https://floating-ui.com/docs/computePosition) - Advanced positioning options
