Skip to main content

Overview

The SelectionBox component enables rectangle selection (also known as “marquee selection” or “lasso selection”) for selecting multiple blocks at once. When the user clicks and drags outside the editor area, a selection rectangle appears, and all blocks that intersect with this rectangle become selected.
Yoopta SelectionBox screenshot

Features

  • ✅ Rectangle selection for multiple blocks
  • ✅ Works outside the editor area
  • ✅ Automatic block selection based on intersection
  • ✅ Supports custom container element
  • ✅ Respects read-only mode
  • ✅ Lightweight and performant

Basic Usage

// Subpath import (recommended)
import { SelectionBox } from '@yoopta/ui/selection-box';
// Or full import
// import { SelectionBox } from '@yoopta/ui';

import { useRef } from 'react';

function App() {
  const selectionBoxRef = useRef<HTMLDivElement>(null);
  const editor = useMemo(() => createYooptaEditor(), []);

  return (
    <div ref={selectionBoxRef}>
      <YooptaEditor editor={editor} plugins={plugins}>
        <SelectionBox selectionBoxElement={selectionBoxRef} />
      </YooptaEditor>
    </div>
  );
}
The selectionBoxElement should be a parent container that wraps the editor. The selection box will listen for mouse events on this element. It must NOT be a child of the editor itself.

API Reference

SelectionBox Component

The main component that renders the selection rectangle and handles mouse events.
<SelectionBox selectionBoxElement={containerRef} />

Props

PropTypeDescription
selectionBoxElementHTMLElement | { current: HTMLElement | null }Container element or ref for mouse event binding

useRectangeSelectionBox Hook

For advanced use cases, you can use the hook directly to build custom selection UI.
import { useRectangeSelectionBox } from '@yoopta/ui/selection-box';

function CustomSelectionBox() {
  const editor = useYooptaEditor();
  const containerRef = useRef<HTMLDivElement>(null);

  const { origin, coords, selection, onClose } = useRectangeSelectionBox({
    editor,
    root: containerRef.current,
  });

  // Build your custom UI using the selection state
  if (!selection) return null;

  return (
    <div
      style={{
        position: 'fixed',
        left: origin[0],
        top: origin[1],
        width: Math.abs(coords[0] - origin[0]),
        height: Math.abs(coords[1] - origin[1]),
        backgroundColor: 'rgba(0, 100, 255, 0.2)',
        border: '1px solid blue',
      }}
    />
  );
}

Hook Parameters

ParameterTypeDescription
editorYooEditorThe Yoopta editor instance
rootHTMLElement | { current: HTMLElement | null }Container element for event binding

Hook Return Value

PropertyTypeDescription
origin[number, number]Starting point coordinates [x, y]
coords[number, number]Current mouse coordinates [x, y]
selectionbooleanWhether a selection is currently in progress
onClose() => voidFunction to close/reset the selection

Types

import type {
  SelectionBoxProps,
  SelectionBoxRoot,
  RectangeSelectionProps,
  RectangeSelectionState,
} from '@yoopta/ui/selection-box';

// Container element type
type SelectionBoxRoot = HTMLElement | { current: HTMLElement | null };

// Component props
type SelectionBoxProps = {
  selectionBoxElement?: SelectionBoxRoot | null;
};

// Hook props
type RectangeSelectionProps = {
  editor: YooEditor;
  root?: SelectionBoxRoot;
};

// Selection state
type RectangeSelectionState = {
  origin: [number, number];
  coords: [number, number];
  selection: boolean;
};

Examples

Basic Setup with Container Ref

import { useRef, useMemo } from 'react';
import { createYooptaEditor, YooptaEditor } from '@yoopta/editor';
import { SelectionBox } from '@yoopta/ui/selection-box';

function EditorWithSelection() {
  const containerRef = useRef<HTMLDivElement>(null);
  const editor = useMemo(() => createYooptaEditor(), []);

  return (
    <div ref={containerRef} className="min-h-screen">
      <YooptaEditor editor={editor} plugins={plugins}>
        <SelectionBox selectionBoxElement={containerRef} />
      </YooptaEditor>
    </div>
  );
}

Full Page Selection

For full-page selection support, pass a ref to the outermost container:
function FullPageEditor() {
  const pageRef = useRef<HTMLDivElement>(null);
  const editor = useMemo(() => createYooptaEditor(), []);

  return (
    <div ref={pageRef} className="min-h-screen p-8">
      <header className="mb-8">
        <h1>My Document</h1>
      </header>

      <main>
        <YooptaEditor editor={editor} plugins={plugins}>
          <SelectionBox selectionBoxElement={pageRef} />
        </YooptaEditor>
      </main>
    </div>
  );
}

Without SelectionBox

If you don’t need rectangle selection, simply don’t include the component:
function SimpleEditor() {
  const editor = useMemo(() => createYooptaEditor(), []);

  return (
    <YooptaEditor editor={editor} plugins={plugins}>
      {/* No SelectionBox - users can still select text normally */}
    </YooptaEditor>
  );
}

How It Works

  1. Mouse Down - When the user clicks outside the editor (but inside the container), the selection starts
  2. Mouse Move - As the user drags, the selection rectangle grows/shrinks
  3. Block Detection - Blocks that intersect with the selection rectangle are marked as selected
  4. Mouse Up - Selection ends, selected blocks remain highlighted
  5. Click Outside - Clicking outside clears the selection
The selected blocks can then be:
  • Copied (Ctrl/Cmd + C)
  • Cut (Ctrl/Cmd + X)
  • Deleted (Backspace or Delete)

Styling

The default selection box has a semi-transparent blue background:
// Default styles applied to the selection box
const selectionBoxStyle = {
  position: 'fixed',
  backgroundColor: 'rgba(35, 131, 226, 0.14)',
  zIndex: 10,
  userSelect: 'none',
};
To customize the appearance, use the useRectangeSelectionBox hook and build your own UI.

Best Practices

Always wrap your editor in a container element and pass that ref to SelectionBox. This ensures selection works in the area outside the editor content.
<div ref={containerRef}>
  <YooptaEditor>
    <SelectionBox selectionBoxElement={containerRef} />
  </YooptaEditor>
</div>
The container should NOT be a child of the editor. The SelectionBox needs to capture mouse events outside the editor content area.
// ❌ Wrong - won't work
<YooptaEditor>
  <div ref={wrongRef}>
    <SelectionBox selectionBoxElement={wrongRef} />
  </div>
</YooptaEditor>

// ✅ Correct
<div ref={correctRef}>
  <YooptaEditor>
    <SelectionBox selectionBoxElement={correctRef} />
  </YooptaEditor>
</div>
Pass the ref object itself, not ref.current. This ensures the component can access the element even if it’s not mounted on the first render.
// ❌ May cause issues on first render
<SelectionBox selectionBoxElement={containerRef.current} />

// ✅ Correct
<SelectionBox selectionBoxElement={containerRef} />

Accessibility

  • Selection box is purely visual and doesn’t affect screen readers
  • Selected blocks are still accessible via keyboard navigation
  • Use keyboard shortcuts (Ctrl/Cmd + A) for full selection as an alternative