---
title: "Floating Element"
description: "A foundational floating UI component for Tiptap editors with intelligent positioning, selection tracking, and automatic visibility management."
canonical_url: "https://tiptap.dev/docs/ui-components/utils-components/floating-element"
---

# Floating Element

A foundational floating UI component for Tiptap editors with intelligent positioning, selection tracking, and automatic visibility management.

A floating UI element that positions itself relative to the current selection in a Tiptap editor. Used for floating toolbars, menus, and other UI elements that need to appear near the text cursor with intelligent positioning and interaction handling.

> **Interactive demo:** [floating element](https://template.tiptap.dev/preview/tiptap-ui-utils/floating-element)

## Installation

Add the component via the Tiptap CLI:

```bash
npx @tiptap/cli@latest add floating-element
```

## Components

### `<FloatingElement />`

A versatile React component that creates floating UI elements positioned relative to text selections in Tiptap editors.

#### Usage

```tsx
import * as React from 'react'
import { EditorContent, EditorContext, useEditor } from '@tiptap/react'

// --- Tiptap Core Extensions ---
import { StarterKit } from '@tiptap/starter-kit'

// --- Tiptap UI ---
import { FloatingElement } from '@/components/tiptap-ui-utils/floating-element'
import { MarkButton } from '@/components/tiptap-ui/mark-button'

// --- UI Primitives ---
import { ButtonGroup } from '@/components/tiptap-ui-primitive/button'
import { Toolbar } from '@/components/tiptap-ui-primitive/toolbar'

// --- Tiptap Node ---
import '@/components/tiptap-node/paragraph-node/paragraph-node.scss'

export const FloatingElementExample = () => {
  const editor = useEditor({
    immediatelyRender: false,
    content: `<h2>Floating Element Example</h2>
      <p>Try selecting some text in this editor. A simple formatting toolbar will appear above your selection. 
      The FloatingElement component positions UI elements relative to the text selection or cursor position. 
      It's commonly used for contextual toolbars, menus, and other elements that should appear near the current editing context.</p>`,
    extensions: [StarterKit],
  })

  return (
    <EditorContext.Provider value={{ editor }}>
      <EditorContent editor={editor} role="presentation" />

      <FloatingElement editor={editor}>
        <Toolbar variant="floating">
          <ButtonGroup orientation="horizontal">
            <MarkButton type="bold" />
            <MarkButton type="italic" />
          </ButtonGroup>
        </Toolbar>
      </FloatingElement>
    </EditorContext.Provider>
  )
}
```

#### Props

| Name                        | Type                                  | Default                    | Description                                                                                                                   |
| --------------------------- | ------------------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `editor`                    | `Editor \| null`                      | `undefined`                | The Tiptap editor instance to attach to                                                                                       |
| `shouldShow`                | `boolean`                             | `undefined`                | Controls whether the floating element should be visible                                                                       |
| `floatingOptions`           | `Partial<UseFloatingOptions>`         | `undefined`                | Additional options to pass to the floating UI                                                                                 |
| `zIndex`                    | `number`                              | `50`                       | Z-index for the floating element                                                                                              |
| `onOpenChange`              | `(open: boolean) => void`             | `undefined`                | Callback fired when the visibility state changes                                                                              |
| `referenceElement`          | `HTMLElement \| null`                 | `undefined`                | Reference element to position the floating element relative to. If provided, this takes precedence over getBoundingClientRect |
| `getBoundingClientRect`     | `(editor: Editor) => DOMRect \| null` | `getSelectionBoundingRect` | Custom function to determine the position of the floating element. Only used if referenceElement is not provided              |
| `closeOnEscape`             | `boolean`                             | `true`                     | Whether to close the floating element when Escape key is pressed                                                              |
| `resetTextSelectionOnClose` | `boolean`                             | `true`                     | When `true` (default), reset/clear the editor's text selection when the floating element closes; when `false`, preserve it    |
| `children`                  | `React.ReactNode`                     | `undefined`                | Content to display inside the floating element                                                                                |

## Advanced Usage Examples

### Basic Floating Toolbar

```tsx
import { shift, flip, offset } from '@floating-ui/react'
import { FloatingElement } from '@/components/tiptap-ui-utils/floating-element'

function FloatingToolbar({ editor }) {
  return (
    <FloatingElement
      editor={editor}
      floatingOptions={{
        placement: 'top',
        middleware: [shift(), flip(), offset(8)],
      }}
    >
      {/* Floating content here */}
    </FloatingElement>
  )
}
```

### Custom Positioning with Mobile Support

```tsx
import { FloatingElement } from '@/components/tiptap-ui-utils/floating-element'
import { useMobile } from '@/hooks/use-mobile'

function ResponsiveFloatingMenu({ editor, isMenuVisible }) {
  const isMobile = useMobile()

  const getCustomRect = (editor) => {
    // Custom positioning logic
    // Example: position relative to current cursor
    return editor.view.coordsAtPos(editor.state.selection.from)
  }

  return (
    <FloatingElement
      editor={editor}
      shouldShow={isMenuVisible}
      getBoundingClientRect={getCustomRect}
      {...(isMobile
        ? {
            style: {
              position: 'fixed',
              left: 0,
              right: 0,
              bottom: 0,
              margin: '.5rem',
              zIndex: 50,
            },
          }
        : {})}
    >
      {/* Floating content here */}
    </FloatingElement>
  )
}
```

### Customize `shouldShow` Floating Menu

```tsx
import { useState, useEffect } from 'react'
import { FloatingElement } from '@/components/tiptap-ui-utils/floating-element'
import { isSelectionValid } from '@/lib/tiptap-collab-utils'

function SelectionMenu({ editor }) {
  const [isVisible, setIsVisible] = useState(false)

  useEffect(() => {
    if (!editor) return

    const updateVisibility = () => {
      const hasSelection = !editor.state.selection.empty
      const isValidSelection = isSelectionValid(editor)
      setIsVisible(hasSelection && isValidSelection)
    }

    editor.on('selectionUpdate', updateVisibility)
    return () => editor.off('selectionUpdate', updateVisibility)
  }, [editor])

  return (
    <FloatingElement editor={editor} shouldShow={isVisible}>
      {/* Your floating content here */}
    </FloatingElement>
  )
}
```

### Using Reference Element

Attach the floating element to a specific DOM element instead of the text selection:

```tsx
import { useState } from 'react'
import { offset, flip, shift } from '@floating-ui/react'
import { FloatingElement } from '@/components/tiptap-ui-utils/floating-element'

function ButtonWithTooltip() {
  const [buttonRef, setButtonRef] = useState<HTMLElement | null>(null)
  const [showTooltip, setShowTooltip] = useState(false)

  return (
    <>
      <button
        ref={setButtonRef}
        onMouseEnter={() => setShowTooltip(true)}
        onMouseLeave={() => setShowTooltip(false)}
      >
        Hover me
      </button>

      <FloatingElement
        editor={editor}
        referenceElement={buttonRef}
        shouldShow={showTooltip}
        floatingOptions={{
          placement: 'top',
          middleware: [offset(8), flip(), shift()],
        }}
      >
        <div className="tooltip">Helpful tooltip content</div>
      </FloatingElement>
    </>
  )
}
```

## Utilities

### `getSelectionBoundingRect(editor)`

Gets the bounding rectangle of the current selection in the editor.

**Parameters:**

- `editor` - The Tiptap editor instance

**Returns:** `DOMRect | null` - The bounding rectangle of the current selection

```tsx
import { getSelectionBoundingRect } from '@/lib/tiptap-collab-utils'

const rect = getSelectionBoundingRect(editor)
console.log('Selection bounds:', rect)
```

### `isSelectionValid(editor, selection?, excludedNodeTypes?)`

Checks if the current selection is valid for showing floating elements. Returns `false` for empty selections, code blocks, excluded node types, and table cells.

**Parameters:**

- `editor` - The Tiptap editor instance
- `selection` (optional) - The selection to validate. Defaults to `editor.state.selection`
- `excludedNodeTypes` (optional) - Array of node type names to exclude. Defaults to `['imageUpload', 'horizontalRule']`

**Returns:** `boolean` - `true` if the selection is valid for floating elements

```tsx
import { isSelectionValid } from '@/lib/tiptap-collab-utils'

const shouldShow = isSelectionValid(editor)

// With custom excluded node types
const isValid = isSelectionValid(editor, undefined, ['image', 'video'])
```

### `isTextSelectionValid(editor)`

Checks if the current text selection is valid for editing. Returns `false` for empty selections, code blocks, and node selections.

**Parameters:**

- `editor` - The Tiptap editor instance

**Returns:** `boolean` - `true` if the text selection is valid

```tsx
import { isTextSelectionValid } from '@/lib/tiptap-collab-utils'

const canEdit = isTextSelectionValid(editor)
if (canEdit) {
  // Show text editing toolbar
}
```

### `isElementWithinEditor(editor, element)`

Checks if a DOM element is within the editor's DOM tree. Useful for determining click/focus events.

**Parameters:**

- `editor` - The Tiptap editor instance
- `element` - The DOM element to check

**Returns:** `boolean` - `true` if the element is within the editor

```tsx
import { isElementWithinEditor } from '@/components/tiptap-ui-utils/floating-element'

const handleClick = (event: MouseEvent) => {
  if (isElementWithinEditor(editor, event.target as Node)) {
    console.log('Clicked inside editor')
  }
}
```
