Skip to main content
The FloatingToolbar component displays a floating toolbar when text is selected, providing quick access to text formatting options like bold, italic, and other marks.

Installation

npm install @yoopta/ui

Basic Usage

import { FloatingToolbar } from '@yoopta/ui';
import { Marks, useYooptaEditor } from '@yoopta/editor';
import { BoldIcon, ItalicIcon } from 'lucide-react';

function MyToolbar() {
  const editor = useYooptaEditor();

  return (
    <FloatingToolbar>
      <FloatingToolbar.Content>
        <FloatingToolbar.Group>
          <FloatingToolbar.Button
            onClick={() => Marks.toggle(editor, { type: 'bold' })}
            active={Marks.isActive(editor, { type: 'bold' })}
          >
            <BoldIcon />
          </FloatingToolbar.Button>
          <FloatingToolbar.Button
            onClick={() => Marks.toggle(editor, { type: 'italic' })}
            active={Marks.isActive(editor, { type: 'italic' })}
          >
            <ItalicIcon />
          </FloatingToolbar.Button>
        </FloatingToolbar.Group>
      </FloatingToolbar.Content>
    </FloatingToolbar>
  );
}

Component API

FloatingToolbar (Root)

The root component that tracks text selection and manages toolbar visibility.
children
ReactNode | ((api: FloatingToolbarApi) => ReactNode)
Toolbar content or render function receiving toolbar state
frozen
boolean
default:"false"
When true, pauses selection tracking (useful when a popover is open)
className
string
Additional CSS class name

FloatingToolbar.Content

The floating content container that renders above the selection.
children
ReactNode
Toolbar buttons and groups
className
string
Additional CSS class name

FloatingToolbar.Group

Groups related buttons together.
children
ReactNode
Toolbar buttons
className
string
Additional CSS class name

FloatingToolbar.Button

children
ReactNode
Button content (usually an icon)
active
boolean
Whether the button is in active state
disabled
boolean
Whether the button is disabled
onClick
(e: MouseEvent) => void
Click handler
className
string
Additional CSS class name

FloatingToolbar.Separator

Visual separator between button groups.
className
string
Additional CSS class name

Examples

Complete Toolbar with Marks

import { FloatingToolbar } from '@yoopta/ui';
import { Marks, useYooptaEditor } from '@yoopta/editor';
import { 
  BoldIcon, 
  ItalicIcon, 
  UnderlineIcon, 
  StrikethroughIcon,
  CodeIcon 
} from 'lucide-react';

function CompleteToolbar() {
  const editor = useYooptaEditor();

  return (
    <FloatingToolbar>
      <FloatingToolbar.Content>
        <FloatingToolbar.Group>
          <FloatingToolbar.Button
            onClick={() => Marks.toggle(editor, { type: 'bold' })}
            active={Marks.isActive(editor, { type: 'bold' })}
            title="Bold (⌘B)"
          >
            <BoldIcon size={16} />
          </FloatingToolbar.Button>
          <FloatingToolbar.Button
            onClick={() => Marks.toggle(editor, { type: 'italic' })}
            active={Marks.isActive(editor, { type: 'italic' })}
            title="Italic (⌘I)"
          >
            <ItalicIcon size={16} />
          </FloatingToolbar.Button>
          <FloatingToolbar.Button
            onClick={() => Marks.toggle(editor, { type: 'underline' })}
            active={Marks.isActive(editor, { type: 'underline' })}
            title="Underline (⌘U)"
          >
            <UnderlineIcon size={16} />
          </FloatingToolbar.Button>
          <FloatingToolbar.Button
            onClick={() => Marks.toggle(editor, { type: 'strike' })}
            active={Marks.isActive(editor, { type: 'strike' })}
            title="Strikethrough"
          >
            <StrikethroughIcon size={16} />
          </FloatingToolbar.Button>
        </FloatingToolbar.Group>
        
        <FloatingToolbar.Separator />
        
        <FloatingToolbar.Group>
          <FloatingToolbar.Button
            onClick={() => Marks.toggle(editor, { type: 'code' })}
            active={Marks.isActive(editor, { type: 'code' })}
            title="Code"
          >
            <CodeIcon size={16} />
          </FloatingToolbar.Button>
        </FloatingToolbar.Group>
      </FloatingToolbar.Content>
    </FloatingToolbar>
  );
}

With Highlight Color Picker

import { FloatingToolbar } from '@yoopta/ui';
import { HighlightColorPicker } from '@yoopta/ui';
import { Marks, useYooptaEditor } from '@yoopta/editor';
import { HighlighterIcon } from 'lucide-react';

function ToolbarWithHighlight() {
  const editor = useYooptaEditor();
  
  const highlightValue = Marks.getValue(editor, { type: 'highlight' }) as
    | { color?: string; backgroundColor?: string }
    | null;

  return (
    <FloatingToolbar>
      <FloatingToolbar.Content>
        <FloatingToolbar.Group>
          <HighlightColorPicker
            value={highlightValue ?? {}}
            presets={['#FFFF00', '#FFE066', '#FFCC99', '#FF9999']}
            onChange={(values) => {
              Marks.add(editor, {
                type: 'highlight',
                value: {
                  color: values.color,
                  backgroundColor: values.backgroundColor,
                },
              });
            }}
          >
            <FloatingToolbar.Button
              active={Marks.isActive(editor, { type: 'highlight' })}
              title="Highlight"
              style={{
                backgroundColor: highlightValue?.backgroundColor,
                color: highlightValue?.color,
              }}
            >
              <HighlighterIcon size={16} />
            </FloatingToolbar.Button>
          </HighlightColorPicker>
        </FloatingToolbar.Group>
      </FloatingToolbar.Content>
    </FloatingToolbar>
  );
}

Frozen State (with Popover)

import { FloatingToolbar } from '@yoopta/ui';
import { useState, useRef } from 'react';
import { ActionMenuList } from '@yoopta/ui';
import { ChevronDownIcon } from 'lucide-react';

function ToolbarWithMenu() {
  const [menuOpen, setMenuOpen] = useState(false);
  const triggerRef = useRef(null);

  return (
    <>
      <FloatingToolbar frozen={menuOpen}>
        <FloatingToolbar.Content>
          <FloatingToolbar.Group>
            <FloatingToolbar.Button
              ref={triggerRef}
              onClick={() => setMenuOpen(true)}
            >
              Turn into
              <ChevronDownIcon size={16} />
            </FloatingToolbar.Button>
          </FloatingToolbar.Group>
        </FloatingToolbar.Content>
      </FloatingToolbar>
      
      <ActionMenuList
        open={menuOpen}
        onOpenChange={setMenuOpen}
        anchor={triggerRef.current}
      />
    </>
  );
}

Render Props Pattern

import { FloatingToolbar } from '@yoopta/ui';

function ConditionalToolbar() {
  return (
    <FloatingToolbar>
      {({ isOpen }) => (
        <FloatingToolbar.Content>
          {isOpen && (
            <FloatingToolbar.Group>
              {/* Buttons */}
            </FloatingToolbar.Group>
          )}
        </FloatingToolbar.Content>
      )}
    </FloatingToolbar>
  );
}

Behavior

Selection Tracking

The toolbar automatically:
  • Shows when text is selected
  • Hides when selection is collapsed
  • Positions above the selected text
  • Handles multi-block selections
  • Respects frozen state

Positioning

Uses Floating UI for smart positioning:
  • Flips to bottom if no space above
  • Shifts horizontally to stay in viewport
  • Updates position on scroll/resize
  • Accounts for inline elements

Click Behavior

Prevents text selection from collapsing on button clicks (especially important on Safari).

Styling

CSS Classes

.yoopta-ui-floating-toolbar {
  /* Toolbar container */
}

.yoopta-ui-floating-toolbar-group {
  /* Button group */
}

.yoopta-ui-floating-toolbar-button {
  /* Individual button */
}

.yoopta-ui-floating-toolbar-button[data-active="true"] {
  /* Active button state */
}

.yoopta-ui-floating-toolbar-separator {
  /* Separator line */
}

Custom Styling

<FloatingToolbar className="my-toolbar">
  <FloatingToolbar.Content className="my-toolbar-content">
    <FloatingToolbar.Button className="my-button">
      {/* ... */}
    </FloatingToolbar.Button>
  </FloatingToolbar.Content>
</FloatingToolbar>

TypeScript

import type {
  FloatingToolbarRootProps,
  FloatingToolbarContentProps,
  FloatingToolbarGroupProps,
  FloatingToolbarButtonProps,
  FloatingToolbarSeparatorProps,
  FloatingToolbarApi,
} from '@yoopta/ui';

See Also

Build docs developers (and LLMs) love