Editor UI Patterns

Users expect modern interactions like floating toolbars and slash commands. Learn how to implement these patterns decoupled from the core editor.

Overview

The UI should be a separate layer from the editor core. It subscribes to editor state changes (selection, focus) and updates its position and visibility.

Floating Toolbar

Appears when text is selected. Shows formatting options (Bold, Italic, Link).

  • Trigger: selectionchange or transaction update.
  • Positioning: Use range.getBoundingClientRect().
  • Logic: Only show if selection is not collapsed and has content.
updateToolbar(selection) {
  if (selection.empty) {
    this.hide();
    return;
  }
  const rect = selection.getRangeAt(0).getBoundingClientRect();
  this.showAt(rect.left, rect.top - toolbarHeight - 10);
}

Slash Menu

Triggered by typing / at the start of a block. Shows a list of blocks to insert.

  • Trigger: input event checking for /.
  • Filtering: Filter commands based on user typing after /.
  • Navigation: Intercept ArrowUp/Down to navigate the menu.

Side/Block Menu

Appears next to the active block (like Notion's 6-dot drag handle). Allows dragging blocks or changing type.

  • Positioning: Track the current active block node.
  • Interaction: Must not steal focus when clicked (use mousedown with preventDefault).